From 3aecceca94cb5e73ff5f04b661caf0ea8da13fb6 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 29 Oct 2018 11:26:08 -0400 Subject: [PATCH 1/3] inference: fix efficiency of tfunc key lookup This was broken when `findfirst` was deprecated in 9bdf07fb4be009427ea6690694782f9a07b83d78. --- base/compiler/optimize.jl | 2 +- base/compiler/tfuncs.jl | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index ec4f03927c354..a42d02b8b70b4 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -305,7 +305,7 @@ function statement_cost(ex::Expr, line::Int, src::CodeInfo, spvals::SimpleVector atyp = argextype(ex.args[3], src, spvals, slottypes) return isknowntype(atyp) ? 4 : params.inline_nonleaf_penalty end - fidx = findfirst(x->x===f, T_FFUNC_KEY) + fidx = find_tfunc(f) if fidx === nothing # unknown/unhandled builtin or anonymous function # Use the generic cost of a direct function call diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index a2115f7157b98..dbcd369d9e6a8 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -18,6 +18,13 @@ const T_IFUNC_COST = Vector{Int}(undef, N_IFUNC) const T_FFUNC_KEY = Vector{Any}() const T_FFUNC_VAL = Vector{Tuple{Int, Int, Any}}() const T_FFUNC_COST = Vector{Int}() +function find_tfunc(@nospecialize f) + for i = 1:length(T_FFUNC_KEY) + if T_FFUNC_KEY[i] === f + return i + end + end +end const DATATYPE_NAME_FIELDINDEX = fieldindex(DataType, :name) const DATATYPE_PARAMETERS_FIELDINDEX = fieldindex(DataType, :parameters) @@ -1248,7 +1255,7 @@ function builtin_tfunction(@nospecialize(f), argtypes::Array{Any,1}, end tf = T_IFUNC[iidx] else - fidx = findfirst(x->x===f, T_FFUNC_KEY) + fidx = find_tfunc(f) if fidx === nothing # unknown/unhandled builtin function return Any From 440a145b31e5af5ceb35de7379b387f900d46b74 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 29 Oct 2018 11:27:51 -0400 Subject: [PATCH 2/3] inference: give all tfuncs names To make accessing them for testing less awkward. --- base/compiler/tfuncs.jl | 103 ++++++++++++++++++++------------------ test/compiler/compiler.jl | 8 ++- 2 files changed, 57 insertions(+), 54 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index dbcd369d9e6a8..37cae35baff90 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -205,23 +205,25 @@ cglobal_tfunc(@nospecialize(fptr)) = Ptr{Cvoid} cglobal_tfunc(@nospecialize(fptr), @nospecialize(t)) = (isType(t) ? Ptr{t.parameters[1]} : Ptr) cglobal_tfunc(@nospecialize(fptr), t::Const) = (isa(t.val, Type) ? Ptr{t.val} : Ptr) add_tfunc(Core.Intrinsics.cglobal, 1, 2, cglobal_tfunc, 5) -add_tfunc(ifelse, 3, 3, - function (@nospecialize(cnd), @nospecialize(x), @nospecialize(y)) - if isa(cnd, Const) - if cnd.val === true - return x - elseif cnd.val === false - return y - else - return Bottom - end - elseif isa(cnd, Conditional) - # optimized (if applicable) in abstract_call - elseif !(Bool ⊑ cnd) + +function ifelse_tfunc(@nospecialize(cnd), @nospecialize(x), @nospecialize(y)) + if isa(cnd, Const) + if cnd.val === true + return x + elseif cnd.val === false + return y + else return Bottom end - return tmerge(x, y) - end, 1) + elseif isa(cnd, Conditional) + # optimized (if applicable) in abstract_call + elseif !(Bool ⊑ cnd) + return Bottom + end + return tmerge(x, y) +end +add_tfunc(ifelse, 3, 3, ifelse_tfunc, 1) + function egal_tfunc(@nospecialize(x), @nospecialize(y)) xx = maybe_widen_conditional(x) yy = maybe_widen_conditional(y) @@ -243,6 +245,7 @@ function egal_tfunc(@nospecialize(x), @nospecialize(y)) return Bool end add_tfunc(===, 2, 2, egal_tfunc, 1) + function isdefined_nothrow(argtypes::Array{Any, 1}) length(argtypes) == 2 || return false return typeintersect(widenconst(argtypes[1]), Module) === Union{} ? @@ -442,23 +445,24 @@ function typeof_tfunc(@nospecialize(t)) end end add_tfunc(typeof, 1, 1, typeof_tfunc, 0) -add_tfunc(typeassert, 2, 2, - function (@nospecialize(v), @nospecialize(t)) - t = instanceof_tfunc(t)[1] - t === Any && return v - if isa(v, Const) - if !has_free_typevars(t) && !isa(v.val, t) - return Bottom - end - return v - elseif isa(v, Conditional) - if !(Bool <: t) - return Bottom - end - return v - end - return typeintersect(widenconst(v), t) - end, 4) + +function typeassert_tfunc(@nospecialize(v), @nospecialize(t)) + t = instanceof_tfunc(t)[1] + t === Any && return v + if isa(v, Const) + if !has_free_typevars(t) && !isa(v.val, t) + return Bottom + end + return v + elseif isa(v, Conditional) + if !(Bool <: t) + return Bottom + end + return v + end + return typeintersect(widenconst(v), t) +end +add_tfunc(typeassert, 2, 2, typeassert_tfunc, 4) function isa_tfunc(@nospecialize(v), @nospecialize(tt)) t, isexact = instanceof_tfunc(tt) @@ -494,23 +498,24 @@ function isa_tfunc(@nospecialize(v), @nospecialize(tt)) return Bool end add_tfunc(isa, 2, 2, isa_tfunc, 0) -add_tfunc(<:, 2, 2, - function (@nospecialize(a), @nospecialize(b)) - a, isexact_a = instanceof_tfunc(a) - b, isexact_b = instanceof_tfunc(b) - if !has_free_typevars(a) && !has_free_typevars(b) - if a <: b - if isexact_b || a === Bottom - return Const(true) - end - else - if isexact_a || (b !== Bottom && typeintersect(a, b) === Union{}) - return Const(false) - end - end - end - return Bool - end, 0) + +function subtype_tfunc(@nospecialize(a), @nospecialize(b)) + a, isexact_a = instanceof_tfunc(a) + b, isexact_b = instanceof_tfunc(b) + if !has_free_typevars(a) && !has_free_typevars(b) + if a <: b + if isexact_b || a === Bottom + return Const(true) + end + else + if isexact_a || (b !== Bottom && typeintersect(a, b) === Union{}) + return Const(false) + end + end + end + return Bool +end +add_tfunc(<:, 2, 2, subtype_tfunc, 0) function const_datatype_getfield_tfunc(@nospecialize(sv), fld::Int) if (fld == DATATYPE_NAME_FIELDINDEX || diff --git a/test/compiler/compiler.jl b/test/compiler/compiler.jl index 5082903501915..73e6ac0d0ead1 100644 --- a/test/compiler/compiler.jl +++ b/test/compiler/compiler.jl @@ -1205,8 +1205,7 @@ isdefined_f3(x) = isdefined(x, 3) @test @inferred(isdefined_f3(())) == false @test find_call(first(code_typed(isdefined_f3, Tuple{Tuple{Vararg{Int}}})[1]), isdefined, 3) -let isa_tfunc = Core.Compiler.T_FFUNC_VAL[ - findfirst(x->x===isa, Core.Compiler.T_FFUNC_KEY)][3] +let isa_tfunc = Core.Compiler.isa_tfunc @test isa_tfunc(Array, Const(AbstractArray)) === Const(true) @test isa_tfunc(Array, Type{AbstractArray}) === Const(true) @test isa_tfunc(Array, Type{AbstractArray{Int}}) == Bool @@ -1220,7 +1219,7 @@ let isa_tfunc = Core.Compiler.T_FFUNC_VAL[ @test isa_tfunc(UnionAll, Const(Type{Array})) === Bool @test isa_tfunc(Union, Const(Union{Float32, Float64})) === Bool @test isa_tfunc(Union, Type{Union}) === Const(true) - @test isa_tfunc(typeof(Union{}), Const(Int)) === Const(false) # any result is ok + @test isa_tfunc(typeof(Union{}), Const(Int)) === Const(false) @test isa_tfunc(typeof(Union{}), Const(Union{})) === Const(false) @test isa_tfunc(typeof(Union{}), typeof(Union{})) === Const(false) @test isa_tfunc(typeof(Union{}), Union{}) === Union{} @@ -1245,8 +1244,7 @@ let isa_tfunc = Core.Compiler.T_FFUNC_VAL[ @test isa_tfunc(Union{Int64, Float64}, Type{AbstractArray}) === Const(false) end -let subtype_tfunc = Core.Compiler.T_FFUNC_VAL[ - findfirst(x->x===(<:), Core.Compiler.T_FFUNC_KEY)][3] +let subtype_tfunc = Core.Compiler.subtype_tfunc @test subtype_tfunc(Type{<:Array}, Const(AbstractArray)) === Const(true) @test subtype_tfunc(Type{<:Array}, Type{AbstractArray}) === Const(true) @test subtype_tfunc(Type{<:Array}, Type{AbstractArray{Int}}) == Bool From e270fbb22c80c3401f0d029c531ce3ed458aa8a5 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 29 Oct 2018 11:41:43 -0400 Subject: [PATCH 3/3] inference: fix egal_tfunc for Conditional Oops. fixes #29836, and adds explicit test coverage --- base/compiler/tfuncs.jl | 3 +- test/compiler/compiler.jl | 66 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 37cae35baff90..cad92ce8157ff 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -230,10 +230,11 @@ function egal_tfunc(@nospecialize(x), @nospecialize(y)) if isa(x, Conditional) && isa(yy, Const) yy.val === false && return Conditional(x.var, x.elsetype, x.vtype) yy.val === true && return x - return x + return Const(false) elseif isa(y, Conditional) && isa(xx, Const) xx.val === false && return Conditional(y.var, y.elsetype, y.vtype) xx.val === true && return y + return Const(false) elseif isa(xx, Const) && isa(yy, Const) return Const(xx.val === yy.val) elseif typeintersect(widenconst(xx), widenconst(yy)) === Bottom diff --git a/test/compiler/compiler.jl b/test/compiler/compiler.jl index 73e6ac0d0ead1..1586055c7adf7 100644 --- a/test/compiler/compiler.jl +++ b/test/compiler/compiler.jl @@ -1222,7 +1222,7 @@ let isa_tfunc = Core.Compiler.isa_tfunc @test isa_tfunc(typeof(Union{}), Const(Int)) === Const(false) @test isa_tfunc(typeof(Union{}), Const(Union{})) === Const(false) @test isa_tfunc(typeof(Union{}), typeof(Union{})) === Const(false) - @test isa_tfunc(typeof(Union{}), Union{}) === Union{} + @test isa_tfunc(typeof(Union{}), Union{}) === Union{} # any result is ok @test isa_tfunc(typeof(Union{}), Type{typeof(Union{})}) === Const(true) @test isa_tfunc(typeof(Union{}), Const(typeof(Union{}))) === Const(true) let c = Conditional(Core.SlotNumber(0), Const(Union{}), Const(Union{})) @@ -1237,7 +1237,7 @@ let isa_tfunc = Core.Compiler.isa_tfunc @test isa_tfunc(Val{1}, Type{Val{T}} where T) === Bool @test isa_tfunc(Val{1}, DataType) === Bool @test isa_tfunc(Any, Const(Any)) === Const(true) - @test isa_tfunc(Any, Union{}) === Union{} + @test isa_tfunc(Any, Union{}) === Union{} # any result is ok @test isa_tfunc(Any, Type{Union{}}) === Const(false) @test isa_tfunc(Union{Int64, Float64}, Type{Real}) === Const(true) @test isa_tfunc(Union{Int64, Float64}, Type{Integer}) === Bool @@ -1294,6 +1294,68 @@ let subtype_tfunc = Core.Compiler.subtype_tfunc @test subtype_tfunc(Union{Type{Int64}, Type{Float64}}, Type{AbstractArray}) === Const(false) end +let egal_tfunc + function egal_tfunc(a, b) + r = Core.Compiler.egal_tfunc(a, b) + @test r === Core.Compiler.egal_tfunc(b, a) + return r + end + @test egal_tfunc(Const(12345.12345), Const(12344.12345 + 1)) == Const(true) + @test egal_tfunc(Array, Const(Array)) === Const(false) + @test egal_tfunc(Array, Type{Array}) === Const(false) + @test egal_tfunc(Int, Int) == Bool + @test egal_tfunc(Array, Array) == Bool + @test egal_tfunc(Array, AbstractArray{Int}) == Bool + @test egal_tfunc(Array{Real}, AbstractArray{Int}) === Const(false) + @test egal_tfunc(Array{Real, 2}, AbstractArray{Real, 2}) === Bool + @test egal_tfunc(Array{Real, 2}, AbstractArray{Int, 2}) === Const(false) + @test egal_tfunc(DataType, Int) === Const(false) + @test egal_tfunc(DataType, Const(Int)) === Bool + @test egal_tfunc(DataType, Const(Array)) === Const(false) + @test egal_tfunc(UnionAll, Const(Int)) === Const(false) + @test egal_tfunc(UnionAll, Const(Array)) === Bool + @test egal_tfunc(Union, Const(Union{Float32, Float64})) === Bool + @test egal_tfunc(Const(Union{Float32, Float64}), Const(Union{Float32, Float64})) === Const(true) + @test egal_tfunc(Type{Union{Float32, Float64}}, Type{Union{Float32, Float64}}) === Bool + @test egal_tfunc(typeof(Union{}), typeof(Union{})) === Bool # could be improved + @test egal_tfunc(Const(typeof(Union{})), Const(typeof(Union{}))) === Const(true) + let c = Conditional(Core.SlotNumber(0), Const(Union{}), Const(Union{})) + @test egal_tfunc(c, Const(Bool)) === Const(false) + @test egal_tfunc(c, Type{Bool}) === Const(false) + @test egal_tfunc(c, Const(Real)) === Const(false) + @test egal_tfunc(c, Type{Real}) === Const(false) + @test egal_tfunc(c, Const(Signed)) === Const(false) + @test egal_tfunc(c, Type{Complex}) === Const(false) + @test egal_tfunc(c, Type{Complex{T}} where T) === Const(false) + @test egal_tfunc(c, Bool) === Bool + @test egal_tfunc(c, Any) === Bool + end + let c = Conditional(Core.SlotNumber(0), Union{}, Const(Union{})) # === Const(false) + @test egal_tfunc(c, Const(false)) === Conditional(c.var, c.elsetype, Union{}) + @test egal_tfunc(c, Const(true)) === Conditional(c.var, Union{}, c.elsetype) + @test egal_tfunc(c, Const(nothing)) === Const(false) + @test egal_tfunc(c, Int) === Const(false) + @test egal_tfunc(c, Bool) === Bool + @test egal_tfunc(c, Any) === Bool + end + let c = Conditional(Core.SlotNumber(0), Const(Union{}), Union{}) # === Const(true) + @test egal_tfunc(c, Const(false)) === Conditional(c.var, Union{}, c.vtype) + @test egal_tfunc(c, Const(true)) === Conditional(c.var, c.vtype, Union{}) + @test egal_tfunc(c, Const(nothing)) === Const(false) + @test egal_tfunc(c, Int) === Const(false) + @test egal_tfunc(c, Bool) === Bool + @test egal_tfunc(c, Any) === Bool + end + @test egal_tfunc(Type{Val{1}}, Type{Val{T}} where T) === Bool + @test egal_tfunc(Type{Val{1}}, DataType) === Bool + @test egal_tfunc(Const(Any), Const(Any)) === Const(true) + @test egal_tfunc(Any, Union{}) === Const(false) # any result is ok + @test egal_tfunc(Type{Any}, Type{Union{}}) === Const(false) + @test egal_tfunc(Union{Int64, Float64}, Real) === Bool + @test egal_tfunc(Union{Int64, Float64}, Integer) === Bool + @test egal_tfunc(Union{Int64, Float64}, AbstractArray) === Const(false) +end + function f23024(::Type{T}, ::Int) where T 1 + 1 end