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

1.8 regression: isbitstype does not resolve at compile-time #309

Closed
maleadt opened this issue Mar 7, 2022 · 7 comments · Fixed by #312
Closed

1.8 regression: isbitstype does not resolve at compile-time #309

maleadt opened this issue Mar 7, 2022 · 7 comments · Fixed by #312

Comments

@maleadt
Copy link
Member

maleadt commented Mar 7, 2022

MWE:

using GPUCompiler

module TestRuntime
    # dummy methods
    signal_exception() = return
    malloc(sz) = C_NULL
    report_oom(sz) = return
    report_exception(ex) = return
    report_exception_name(ex) = return
    report_exception_frame(idx, func, file, line) = return
end

struct TestCompilerParams <: AbstractCompilerParams end
GPUCompiler.runtime_module(::CompilerJob{<:Any,TestCompilerParams}) = TestRuntime

function kernel(ptr::Ptr{T}, val) where {T}
    if isbitstype(T)
        unsafe_store!(ptr, val, 1)
    end
    return
end

function main()
    source = FunctionSpec(kernel, Tuple{Ptr{Float32}, Float32})
    target = NativeCompilerTarget()
    params = TestCompilerParams()
    job = CompilerJob(target, source, params)

    JuliaContext() do ctx
        println(GPUCompiler.compile(:llvm, job; ctx)[1])
    end
end

isinteractive() || main()

On 1.9:

define void @_Z16julia_kernel_6353PtrI7Float32ES0_(i64 zeroext %0, float %1) local_unnamed_addr #0 !dbg !5 {
top:
  %2 = load i8, i8* inttoptr (i64 4914551176 to i8*), align 8, !dbg !7, !tbaa !14
  %3 = and i8 %2, 8, !dbg !18
  %.not = icmp eq i8 %3, 0, !dbg !21
  br i1 %.not, label %L7, label %L6, !dbg !13

L6:                                               ; preds = %top
  %4 = inttoptr i64 %0 to float*, !dbg !24
  store float %1, float* %4, align 1, !dbg !24, !tbaa !28
  br label %L7, !dbg !24

L7:                                               ; preds = %L6, %top
  ret void, !dbg !30
}

... which is Really Bad, since it's leaking CPU values into the GPU IR.

For reference, on 1.8:

define void @_Z16julia_kernel_4963PtrI7Float32ES0_(i64 zeroext %0, float %1) local_unnamed_addr #0 !dbg !5 {
top:
  %2 = inttoptr i64 %0 to float*, !dbg !7
  store float %1, float* %2, align 1, !dbg !7, !tbaa !11
  ret void, !dbg !15
}

Enabling inference remarks gives:

┌ Debug: Inference remark during GPU compilation of MethodInstance for isbitstype(::Type{Float32}): [constprop] Disabled by function heuristic
└ @ GPUCompiler ~/Julia/pkg/GPUCompiler/src/utils.jl:35
┌ Debug: Inference remark during GPU compilation of MethodInstance for kernel(::Ptr{Float32}, ::Float32): [constprop] Disabled by argument and rettype heuristics
└ @ GPUCompiler ~/Julia/pkg/GPUCompiler/src/utils.jl:35
┌ Debug: Inference remark during GPU compilation of MethodInstance for unsafe_store!(::Ptr{Float32}, ::Float32, ::Int64): [constprop] Disabled by argument and rettype heuristics
└ @ GPUCompiler ~/Julia/pkg/GPUCompiler/src/utils.jl:35
┌ Debug: Inference remark during GPU compilation of MethodInstance for Int64(::Int64): [constprop] Disabled by argument and rettype heuristics
└ @ GPUCompiler ~/Julia/pkg/GPUCompiler/src/utils.jl:35
┌ Debug: Inference remark during GPU compilation of MethodInstance for unsafe_store!(::Ptr{Float32}, ::Float32, ::Int64): [constprop] Disabled by argument and rettype heuristics
└ @ GPUCompiler ~/Julia/pkg/GPUCompiler/src/utils.jl:35
┌ Debug: Inference remark during GPU compilation of MethodInstance for unsafe_store!(::Ptr{Float32}, ::Float32, ::Int64): [constprop] Disabled by argument and rettype heuristics
└ @ GPUCompiler ~/Julia/pkg/GPUCompiler/src/utils.jl:35
┌ Debug: Inference remark during GPU compilation of MethodInstance for kernel(::Ptr{Float32}, ::Float32): Call result type was widened because the return value is unused
└ @ GPUCompiler ~/Julia/pkg/GPUCompiler/src/utils.jl:35

Const-prop of isbitstype being disabled seems crucial here. I wonder if this has anything to do with the fix to JuliaLang/julia@4846fbb (cc @aviatesk).

@vchuravy
Copy link
Member

vchuravy commented Mar 7, 2022

The failure for allocatedinline #302 (comment) is potentially related.

@maleadt
Copy link
Member Author

maleadt commented Mar 7, 2022

Defining isoverlayed(::OverlayMethodTable) = false makes this infer properly again, so this is definitely related to JuliaLang/julia#44224

@aviatesk
Copy link
Contributor

aviatesk commented Mar 8, 2022

I will work on it today.

aviatesk added a commit to JuliaLang/julia that referenced this issue Mar 8, 2022
… `AbstractInterpreter` with overlayed method table

Built on top of #44511, and solves <JuliaGPU/GPUCompiler.jl#309>.
This commit allows external `AbstractInterpreter` to use pure/concrete
evals even if it uses an overlayed method table. More specifically, such
`AbstractInterpreter` can use pure/concrete evals as far as any matching
methods in question doesn't come from the overlayed method table:
```julia
@test Base.return_types((), MTOverlayInterp()) do
    isbitstype(Int) ? nothing : missing
end == Any[Nothing]
Base.@assume_effects :terminates_globally function issue41694(x)
    res = 1
    1 < x < 20 || throw("bad")
    while x > 1
        res *= x
        x -= 1
    end
    return res
end
@test Base.return_types((), MTOverlayInterp()) do
    issue41694(3) == 6 ? nothing : missing
end == Any[Nothing]
```
@aviatesk
Copy link
Contributor

aviatesk commented Mar 8, 2022

JuliaLang/julia#44515 will beat this out.

@maleadt
Copy link
Member Author

maleadt commented Mar 8, 2022

Thanks! Tested the PR, and works great :-)

@maleadt
Copy link
Member Author

maleadt commented Mar 8, 2022

And yes this also fixes #302 (comment)

aviatesk added a commit to JuliaLang/julia that referenced this issue Mar 9, 2022
… `AbstractInterpreter` with overlayed method table

Built on top of #44511, and solves <JuliaGPU/GPUCompiler.jl#309>.
This commit allows external `AbstractInterpreter` to use pure/concrete
evals even if it uses an overlayed method table. More specifically, such
`AbstractInterpreter` can use pure/concrete evals as far as any matching
methods in question doesn't come from the overlayed method table:
```julia
@test Base.return_types((), MTOverlayInterp()) do
    isbitstype(Int) ? nothing : missing
end == Any[Nothing]
Base.@assume_effects :terminates_globally function issue41694(x)
    res = 1
    1 < x < 20 || throw("bad")
    while x > 1
        res *= x
        x -= 1
    end
    return res
end
@test Base.return_types((), MTOverlayInterp()) do
    issue41694(3) == 6 ? nothing : missing
end == Any[Nothing]
```
aviatesk added a commit to JuliaLang/julia that referenced this issue Mar 9, 2022
… `AbstractInterpreter` with overlayed method table

Built on top of #44511, and solves <JuliaGPU/GPUCompiler.jl#309>.
This commit allows external `AbstractInterpreter` to use pure/concrete
evals even if it uses an overlayed method table. More specifically, such
`AbstractInterpreter` can use pure/concrete evals as far as any matching
methods in question doesn't come from the overlayed method table:
```julia
@test Base.return_types((), MTOverlayInterp()) do
    isbitstype(Int) ? nothing : missing
end == Any[Nothing]
Base.@assume_effects :terminates_globally function issue41694(x)
    res = 1
    1 < x < 20 || throw("bad")
    while x > 1
        res *= x
        x -= 1
    end
    return res
end
@test Base.return_types((), MTOverlayInterp()) do
    issue41694(3) == 6 ? nothing : missing
end == Any[Nothing]
```
aviatesk added a commit to JuliaLang/julia that referenced this issue Mar 11, 2022
… `AbstractInterpreter` with overlayed method table

Built on top of #44511, and solves <JuliaGPU/GPUCompiler.jl#309>.
This commit allows external `AbstractInterpreter` to use pure/concrete
evals even if it uses an overlayed method table. More specifically, such
`AbstractInterpreter` can use pure/concrete evals as far as any matching
methods in question doesn't come from the overlayed method table:
```julia
@test Base.return_types((), MTOverlayInterp()) do
    isbitstype(Int) ? nothing : missing
end == Any[Nothing]
Base.@assume_effects :terminates_globally function issue41694(x)
    res = 1
    1 < x < 20 || throw("bad")
    while x > 1
        res *= x
        x -= 1
    end
    return res
end
@test Base.return_types((), MTOverlayInterp()) do
    issue41694(3) == 6 ? nothing : missing
end == Any[Nothing]
```
aviatesk added a commit to JuliaLang/julia that referenced this issue Mar 14, 2022
… `AbstractInterpreter` with overlayed method table

Built on top of #44511, and solves <JuliaGPU/GPUCompiler.jl#309>.
This commit allows external `AbstractInterpreter` to use pure/concrete
evals even if it uses an overlayed method table. More specifically, such
`AbstractInterpreter` can use pure/concrete evals as far as any matching
methods in question doesn't come from the overlayed method table:
```julia
@test Base.return_types((), MTOverlayInterp()) do
    isbitstype(Int) ? nothing : missing
end == Any[Nothing]
Base.@assume_effects :terminates_globally function issue41694(x)
    res = 1
    1 < x < 20 || throw("bad")
    while x > 1
        res *= x
        x -= 1
    end
    return res
end
@test Base.return_types((), MTOverlayInterp()) do
    issue41694(3) == 6 ? nothing : missing
end == Any[Nothing]
```
aviatesk added a commit to JuliaLang/julia that referenced this issue Mar 14, 2022
…al `AbstractInterpreter` with overlayed method table

Built on top of #44511 and #44561, and solves <JuliaGPU/GPUCompiler.jl#309>.
This commit allows external `AbstractInterpreter` to selectively use
pure/concrete evals even if it uses an overlayed method table.
More specifically, such `AbstractInterpreter` can use pure/concrete evals
as far as any callees used in a call in question doesn't come from the
overlayed method table:
```julia
@test Base.return_types((), MTOverlayInterp()) do
    isbitstype(Int) ? nothing : missing
end == Any[Nothing]
Base.@assume_effects :terminates_globally function issue41694(x)
    res = 1
    1 < x < 20 || throw("bad")
    while x > 1
        res *= x
        x -= 1
    end
    return res
end
@test Base.return_types((), MTOverlayInterp()) do
    issue41694(3) == 6 ? nothing : missing
end == Any[Nothing]
```

In order to check if a call is tainted by any overlayed call, our effect
system now additionally tracks `overlayed::Bool` property. This effect
property is required to prevents concrete-eval in the following kind of situation:
```julia
strangesin(x) = sin(x)
@overlay OverlayedMT strangesin(x::Float64) = iszero(x) ? nothing : cos(x)
Base.@assume_effects :total totalcall(f, args...) = f(args...)
@test Base.return_types(; interp=MTOverlayInterp()) do
    # we need to disable partial pure/concrete evaluation when tainted by any overlayed call
    if totalcall(strangesin, 1.0) == cos(1.0)
        return nothing
    else
        return missing
    end
end |> only === Nothing
```
aviatesk added a commit to JuliaLang/julia that referenced this issue Mar 14, 2022
…al `AbstractInterpreter` with overlayed method table

Built on top of #44511 and #44561, and solves <JuliaGPU/GPUCompiler.jl#309>.
This commit allows external `AbstractInterpreter` to selectively use
pure/concrete evals even if it uses an overlayed method table.
More specifically, such `AbstractInterpreter` can use pure/concrete evals
as far as any callees used in a call in question doesn't come from the
overlayed method table:
```julia
@test Base.return_types((), MTOverlayInterp()) do
    isbitstype(Int) ? nothing : missing
end == Any[Nothing]
Base.@assume_effects :terminates_globally function issue41694(x)
    res = 1
    1 < x < 20 || throw("bad")
    while x > 1
        res *= x
        x -= 1
    end
    return res
end
@test Base.return_types((), MTOverlayInterp()) do
    issue41694(3) == 6 ? nothing : missing
end == Any[Nothing]
```

In order to check if a call is tainted by any overlayed call, our effect
system now additionally tracks `overlayed::Bool` property. This effect
property is required to prevents concrete-eval in the following kind of situation:
```julia
strangesin(x) = sin(x)
@overlay OverlayedMT strangesin(x::Float64) = iszero(x) ? nothing : cos(x)
Base.@assume_effects :total totalcall(f, args...) = f(args...)
@test Base.return_types(; interp=MTOverlayInterp()) do
    # we need to disable partial pure/concrete evaluation when tainted by any overlayed call
    if totalcall(strangesin, 1.0) == cos(1.0)
        return nothing
    else
        return missing
    end
end |> only === Nothing
```
aviatesk added a commit to JuliaLang/julia that referenced this issue Mar 14, 2022
…al `AbstractInterpreter` with overlayed method table

Built on top of #44511 and #44561, and solves <JuliaGPU/GPUCompiler.jl#309>.
This commit allows external `AbstractInterpreter` to selectively use
pure/concrete evals even if it uses an overlayed method table.
More specifically, such `AbstractInterpreter` can use pure/concrete evals
as far as any callees used in a call in question doesn't come from the
overlayed method table:
```julia
@test Base.return_types((), MTOverlayInterp()) do
    isbitstype(Int) ? nothing : missing
end == Any[Nothing]
Base.@assume_effects :terminates_globally function issue41694(x)
    res = 1
    1 < x < 20 || throw("bad")
    while x > 1
        res *= x
        x -= 1
    end
    return res
end
@test Base.return_types((), MTOverlayInterp()) do
    issue41694(3) == 6 ? nothing : missing
end == Any[Nothing]
```

In order to check if a call is tainted by any overlayed call, our effect
system now additionally tracks `overlayed::Bool` property. This effect
property is required to prevents concrete-eval in the following kind of situation:
```julia
strangesin(x) = sin(x)
@overlay OverlayedMT strangesin(x::Float64) = iszero(x) ? nothing : cos(x)
Base.@assume_effects :total totalcall(f, args...) = f(args...)
@test Base.return_types(; interp=MTOverlayInterp()) do
    # we need to disable partial pure/concrete evaluation when tainted by any overlayed call
    if totalcall(strangesin, 1.0) == cos(1.0)
        return nothing
    else
        return missing
    end
end |> only === Nothing
```
aviatesk added a commit to JuliaLang/julia that referenced this issue Mar 14, 2022
…al `AbstractInterpreter` with overlayed method table

Built on top of #44511 and #44561, and solves <JuliaGPU/GPUCompiler.jl#309>.
This commit allows external `AbstractInterpreter` to selectively use
pure/concrete evals even if it uses an overlayed method table.
More specifically, such `AbstractInterpreter` can use pure/concrete evals
as far as any callees used in a call in question doesn't come from the
overlayed method table:
```julia
@test Base.return_types((), MTOverlayInterp()) do
    isbitstype(Int) ? nothing : missing
end == Any[Nothing]
Base.@assume_effects :terminates_globally function issue41694(x)
    res = 1
    1 < x < 20 || throw("bad")
    while x > 1
        res *= x
        x -= 1
    end
    return res
end
@test Base.return_types((), MTOverlayInterp()) do
    issue41694(3) == 6 ? nothing : missing
end == Any[Nothing]
```

In order to check if a call is tainted by any overlayed call, our effect
system now additionally tracks `overlayed::Bool` property. This effect
property is required to prevents concrete-eval in the following kind of situation:
```julia
strangesin(x) = sin(x)
@overlay OverlayedMT strangesin(x::Float64) = iszero(x) ? nothing : cos(x)
Base.@assume_effects :total totalcall(f, args...) = f(args...)
@test Base.return_types(; interp=MTOverlayInterp()) do
    # we need to disable partial pure/concrete evaluation when tainted by any overlayed call
    if totalcall(strangesin, 1.0) == cos(1.0)
        return nothing
    else
        return missing
    end
end |> only === Nothing
```
aviatesk added a commit to JuliaLang/julia that referenced this issue Mar 14, 2022
…al `AbstractInterpreter` with overlayed method table

Built on top of #44511 and #44561, and solves <JuliaGPU/GPUCompiler.jl#309>.
This commit allows external `AbstractInterpreter` to selectively use
pure/concrete evals even if it uses an overlayed method table.
More specifically, such `AbstractInterpreter` can use pure/concrete evals
as far as any callees used in a call in question doesn't come from the
overlayed method table:
```julia
@test Base.return_types((), MTOverlayInterp()) do
    isbitstype(Int) ? nothing : missing
end == Any[Nothing]
Base.@assume_effects :terminates_globally function issue41694(x)
    res = 1
    1 < x < 20 || throw("bad")
    while x > 1
        res *= x
        x -= 1
    end
    return res
end
@test Base.return_types((), MTOverlayInterp()) do
    issue41694(3) == 6 ? nothing : missing
end == Any[Nothing]
```

In order to check if a call is tainted by any overlayed call, our effect
system now additionally tracks `overlayed::Bool` property. This effect
property is required to prevents concrete-eval in the following kind of situation:
```julia
strangesin(x) = sin(x)
@overlay OverlayedMT strangesin(x::Float64) = iszero(x) ? nothing : cos(x)
Base.@assume_effects :total totalcall(f, args...) = f(args...)
@test Base.return_types(; interp=MTOverlayInterp()) do
    # we need to disable partial pure/concrete evaluation when tainted by any overlayed call
    if totalcall(strangesin, 1.0) == cos(1.0)
        return nothing
    else
        return missing
    end
end |> only === Nothing
```
aviatesk added a commit to JuliaLang/julia that referenced this issue Mar 14, 2022
…al `AbstractInterpreter` with overlayed method table

Built on top of #44511 and #44561, and solves <JuliaGPU/GPUCompiler.jl#309>.
This commit allows external `AbstractInterpreter` to selectively use
pure/concrete evals even if it uses an overlayed method table.
More specifically, such `AbstractInterpreter` can use pure/concrete evals
as far as any callees used in a call in question doesn't come from the
overlayed method table:
```julia
@test Base.return_types((), MTOverlayInterp()) do
    isbitstype(Int) ? nothing : missing
end == Any[Nothing]
Base.@assume_effects :terminates_globally function issue41694(x)
    res = 1
    1 < x < 20 || throw("bad")
    while x > 1
        res *= x
        x -= 1
    end
    return res
end
@test Base.return_types((), MTOverlayInterp()) do
    issue41694(3) == 6 ? nothing : missing
end == Any[Nothing]
```

In order to check if a call is tainted by any overlayed call, our effect
system now additionally tracks `overlayed::Bool` property. This effect
property is required to prevents concrete-eval in the following kind of situation:
```julia
strangesin(x) = sin(x)
@overlay OverlayedMT strangesin(x::Float64) = iszero(x) ? nothing : cos(x)
Base.@assume_effects :total totalcall(f, args...) = f(args...)
@test Base.return_types(; interp=MTOverlayInterp()) do
    # we need to disable partial pure/concrete evaluation when tainted by any overlayed call
    if totalcall(strangesin, 1.0) == cos(1.0)
        return nothing
    else
        return missing
    end
end |> only === Nothing
```
aviatesk added a commit to JuliaLang/julia that referenced this issue Mar 15, 2022
…al `AbstractInterpreter` with overlayed method table

Built on top of #44511 and #44561, and solves <JuliaGPU/GPUCompiler.jl#309>.
This commit allows external `AbstractInterpreter` to selectively use
pure/concrete evals even if it uses an overlayed method table.
More specifically, such `AbstractInterpreter` can use pure/concrete evals
as far as any callees used in a call in question doesn't come from the
overlayed method table:
```julia
@test Base.return_types((), MTOverlayInterp()) do
    isbitstype(Int) ? nothing : missing
end == Any[Nothing]
Base.@assume_effects :terminates_globally function issue41694(x)
    res = 1
    1 < x < 20 || throw("bad")
    while x > 1
        res *= x
        x -= 1
    end
    return res
end
@test Base.return_types((), MTOverlayInterp()) do
    issue41694(3) == 6 ? nothing : missing
end == Any[Nothing]
```

In order to check if a call is tainted by any overlayed call, our effect
system now additionally tracks `overlayed::Bool` property. This effect
property is required to prevents concrete-eval in the following kind of situation:
```julia
strangesin(x) = sin(x)
@overlay OverlayedMT strangesin(x::Float64) = iszero(x) ? nothing : cos(x)
Base.@assume_effects :total totalcall(f, args...) = f(args...)
@test Base.return_types(; interp=MTOverlayInterp()) do
    # we need to disable partial pure/concrete evaluation when tainted by any overlayed call
    if totalcall(strangesin, 1.0) == cos(1.0)
        return nothing
    else
        return missing
    end
end |> only === Nothing
```
aviatesk added a commit to JuliaLang/julia that referenced this issue Mar 15, 2022
…al `AbstractInterpreter` with overlayed method table

Built on top of #44511 and #44561, and solves <JuliaGPU/GPUCompiler.jl#309>.
This commit allows external `AbstractInterpreter` to selectively use
pure/concrete evals even if it uses an overlayed method table.
More specifically, such `AbstractInterpreter` can use pure/concrete evals
as far as any callees used in a call in question doesn't come from the
overlayed method table:
```julia
@test Base.return_types((), MTOverlayInterp()) do
    isbitstype(Int) ? nothing : missing
end == Any[Nothing]
Base.@assume_effects :terminates_globally function issue41694(x)
    res = 1
    1 < x < 20 || throw("bad")
    while x > 1
        res *= x
        x -= 1
    end
    return res
end
@test Base.return_types((), MTOverlayInterp()) do
    issue41694(3) == 6 ? nothing : missing
end == Any[Nothing]
```

In order to check if a call is tainted by any overlayed call, our effect
system now additionally tracks `overlayed::Bool` property. This effect
property is required to prevents concrete-eval in the following kind of situation:
```julia
strangesin(x) = sin(x)
@overlay OverlayedMT strangesin(x::Float64) = iszero(x) ? nothing : cos(x)
Base.@assume_effects :total totalcall(f, args...) = f(args...)
@test Base.return_types(; interp=MTOverlayInterp()) do
    # we need to disable partial pure/concrete evaluation when tainted by any overlayed call
    if totalcall(strangesin, 1.0) == cos(1.0)
        return nothing
    else
        return missing
    end
end |> only === Nothing
```
@aviatesk
Copy link
Contributor

This can now be closed.

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 a pull request may close this issue.

3 participants