From bb52621dfad3acba5168688ceccb74841fb41483 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 23 Jun 2021 22:47:57 -0400 Subject: [PATCH] inference: Add some basic inference for PhiNode (#41115) PhiNode isn't really supported in lowered IR, but it is convenient to be able to emit it, particularly for code that is designed to perform transformations both on typed and on lowered IR (such as Diffractor). Moreover, we do actually supported phinodes in untyped IR both in the interpreter and in the compiler. At the moment, inference assumes that PhiNodes get treated as embedded constants, which is just not what the actual implementation does. At the very least, it should just leave PhiNodes alone in case they accidentally end up in lowered IR (rather than causing crashes), but we might as well give them some basic support. --- base/compiler/abstractinterpretation.jl | 7 +++++ base/compiler/optimize.jl | 39 +++++++++++++++++++++---- base/compiler/typeinfer.jl | 4 +-- base/compiler/utilities.jl | 10 +++++++ src/codegen.cpp | 8 +++-- test/compiler/inference.jl | 32 ++++++++++++++++++++ 6 files changed, 89 insertions(+), 11 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index ededc5111104e..dc4663a877d9a 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1421,6 +1421,13 @@ end function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState) if !isa(e, Expr) + if isa(e, PhiNode) + rt = Union{} + for val in e.values + rt = tmerge(rt, abstract_eval_special_value(interp, val, vtypes, sv)) + end + return rt + end return abstract_eval_special_value(interp, e, vtypes, sv) end e = e::Expr diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 2d4f0d022fd6d..6d059247a43ea 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -593,14 +593,23 @@ function renumber_ir_elements!(body::Vector{Any}, changemap::Vector{Int}) return renumber_ir_elements!(body, changemap, changemap) end -function renumber_ir_elements!(body::Vector{Any}, ssachangemap::Vector{Int}, labelchangemap::Vector{Int}) - for i = 2:length(labelchangemap) - labelchangemap[i] += labelchangemap[i - 1] +function cumsum_ssamap!(ssamap::Vector{Int}) + rel_change = 0 + for i = 1:length(ssamap) + rel_change += ssamap[i] + if ssamap[i] == -1 + # Keep a marker that this statement was deleted + ssamap[i] = typemin(Int) + else + ssamap[i] = rel_change + end end +end + +function renumber_ir_elements!(body::Vector{Any}, ssachangemap::Vector{Int}, labelchangemap::Vector{Int}) + cumsum_ssamap!(labelchangemap) if ssachangemap !== labelchangemap - for i = 2:length(ssachangemap) - ssachangemap[i] += ssachangemap[i - 1] - end + cumsum_ssamap!(ssachangemap) end if labelchangemap[end] == 0 && ssachangemap[end] == 0 return @@ -621,6 +630,24 @@ function renumber_ir_elements!(body::Vector{Any}, ssachangemap::Vector{Int}, lab end elseif isa(el, SSAValue) body[i] = SSAValue(el.id + ssachangemap[el.id]) + elseif isa(el, PhiNode) + i = 1 + edges = el.edges + values = el.values + while i <= length(edges) + was_deleted = ssachangemap[edges[i]] == typemin(Int) + if was_deleted + deleteat!(edges, i) + deleteat!(values, i) + else + edges[i] += ssachangemap[edges[i]] + val = values[i] + if isa(val, SSAValue) + values[i] = SSAValue(val.id + ssachangemap[val.id]) + end + i += 1 + end + end elseif isa(el, Expr) if el.head === :(=) && el.args[2] isa Expr el = el.args[2]::Expr diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 3fa2f20006fec..4ad96ae2e72f0 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -672,9 +672,7 @@ function type_annotate!(sv::InferenceState, run_optimizer::Bool) deleteat!(src.codelocs, i) deleteat!(sv.stmt_info, i) nexpr -= 1 - if oldidx < length(changemap) - changemap[oldidx + 1] = -1 - end + changemap[oldidx] = -1 continue else body[i] = Const(expr) # annotate that this statement actually is dead diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 4e9d2b74cf40c..3b84395c676d2 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -267,6 +267,8 @@ function find_ssavalue_uses(body::Vector{Any}, nvals::Int) push!(uses[e.id], line) elseif isa(e, Expr) find_ssavalue_uses(e, uses, line) + elseif isa(e, PhiNode) + find_ssavalue_uses(e, uses, line) end end return uses @@ -287,6 +289,14 @@ function find_ssavalue_uses(e::Expr, uses::Vector{BitSet}, line::Int) end end +function find_ssavalue_uses(e::PhiNode, uses::Vector{BitSet}, line::Int) + for val in e.values + if isa(val, SSAValue) + push!(uses[val.id], line) + end + end +end + function is_throw_call(e::Expr) if e.head === :call f = e.args[1] diff --git a/src/codegen.cpp b/src/codegen.cpp index cba7d1d2d3080..4eba89badbd8e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4087,9 +4087,13 @@ static void emit_vi_assignment_unboxed(jl_codectx_t &ctx, jl_varinfo_t &vi, Valu static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) { jl_value_t *ssavalue_types = (jl_value_t*)ctx.source->ssavaluetypes; - assert(jl_is_array(ssavalue_types)); + jl_value_t *phiType = NULL; + if (jl_is_array(ssavalue_types)) { + phiType = jl_array_ptr_ref(ssavalue_types, idx); + } else { + phiType = (jl_value_t*)jl_any_type; + } jl_array_t *edges = (jl_array_t*)jl_fieldref_noalloc(r, 0); - jl_value_t *phiType = jl_array_ptr_ref(ssavalue_types, idx); BasicBlock *BB = ctx.builder.GetInsertBlock(); auto InsertPt = BB->getFirstInsertionPt(); if (phiType == jl_bottom_type) { diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 52602a58c517b..d5bad34e38322 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -3323,3 +3323,35 @@ end |> first === Tuple{Int, String} primitive type UInt24ish 24 end f34288(x) = Core.Intrinsics.checked_sdiv_int(x, Core.Intrinsics.trunc_int(UInt24ish, 0)) @test Base.return_types(f34288, (UInt24ish,)) == Any[UInt24ish] + +# Inference of PhiNode showing up in lowered AST +function f_convert_me_to_ir(b, x) + a = b ? sin(x) : cos(x) + return a +end + +let + # Test the presence of PhiNodes in lowered IR by taking the above function, + # running it through SSA conversion and then putting it into an opaque + # closure. + mi = Core.Compiler.specialize_method(first(methods(f_convert_me_to_ir)), + Tuple{Bool, Float64}, Core.svec()) + ci = Base.uncompressed_ast(mi.def) + ci.ssavaluetypes = Any[Any for i = 1:ci.ssavaluetypes] + sv = Core.Compiler.OptimizationState(mi, Core.Compiler.OptimizationParams(), + Core.Compiler.NativeInterpreter()) + ir = Core.Compiler.convert_to_ircode(ci, Core.Compiler.copy_exprargs(ci.code), + false, 2, sv) + ir = Core.Compiler.slot2reg(ir, ci, 2, sv) + ir = Core.Compiler.compact!(ir) + Core.Compiler.replace_code_newstyle!(ci, ir, 3) + ci.ssavaluetypes = length(ci.code) + @test any(x->isa(x, Core.PhiNode), ci.code) + oc = @eval b->$(Expr(:new_opaque_closure, Tuple{Bool, Float64}, false, Any, Any, + Expr(:opaque_closure_method, nothing, 2, LineNumberNode(0, nothing), ci)))(b, 1.0) + @test Base.return_types(oc, Tuple{Bool}) == Any[Float64] + + oc = @eval ()->$(Expr(:new_opaque_closure, Tuple{Bool, Float64}, false, Any, Any, + Expr(:opaque_closure_method, nothing, 2, LineNumberNode(0, nothing), ci)))(true, 1.0) + @test Base.return_types(oc, Tuple{}) == Any[Float64] +end