Skip to content

Commit

Permalink
Revert "inference: follow up #43852 (#44101)"
Browse files Browse the repository at this point in the history
This reverts commit fed4544.
  • Loading branch information
DilumAluthge committed Feb 11, 2022
1 parent 32a026a commit d255809
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 129 deletions.
9 changes: 5 additions & 4 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ struct MethodCallResult
end
end

function is_all_const_arg((; argtypes)::ArgInfo)
function is_all_const_arg((; fargs, argtypes)::ArgInfo)
for a in argtypes
if !isa(a, Const) && !isconstType(a) && !issingletontype(a)
return false
Expand All @@ -628,8 +628,9 @@ function is_all_const_arg((; argtypes)::ArgInfo)
return true
end

function concrete_eval_const_proven_total_or_error(interp::AbstractInterpreter,
@nospecialize(f), (; argtypes)::ArgInfo, _::InferenceState)
function concrete_eval_const_proven_total_or_error(
interp::AbstractInterpreter,
@nospecialize(f), argtypes::Vector{Any})
args = Any[ (a = widenconditional(argtypes[i]);
isa(a, Const) ? a.val :
isconstType(a) ? (a::DataType).parameters[1] :
Expand Down Expand Up @@ -672,7 +673,7 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, resul
return nothing
end
if f !== nothing && result.edge !== nothing && is_total_or_error(result.edge_effects) && is_all_const_arg(arginfo)
rt = concrete_eval_const_proven_total_or_error(interp, f, arginfo, sv)
rt = concrete_eval_const_proven_total_or_error(interp, f, arginfo.argtypes)
add_backedge!(result.edge, sv)
if rt === nothing
# The evaulation threw. By :consistent-cy, we're guaranteed this would have happened at runtime
Expand Down
60 changes: 27 additions & 33 deletions base/compiler/ssair/inlining.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1034,22 +1034,18 @@ function inline_invoke!(
# TODO: We could union split out the signature check and continue on
return nothing
end
argtypes = invoke_rewrite(sig.argtypes)
result = info.result
if isa(result, ConstResult)
item = const_result_item(result, state)
else
argtypes = invoke_rewrite(sig.argtypes)
if isa(result, InferenceResult)
(; mi) = item = InliningTodo(result, argtypes)
validate_sparams(mi.sparam_vals) || return nothing
if argtypes_to_type(argtypes) <: mi.def.sig
state.mi_cache !== nothing && (item = resolve_todo(item, state, flag))
handle_single_case!(ir, idx, stmt, item, todo, state.params, true)
return nothing
end
if isa(result, InferenceResult)
(; mi) = item = InliningTodo(result, argtypes)
validate_sparams(mi.sparam_vals) || return nothing
if argtypes_to_type(argtypes) <: mi.def.sig
state.mi_cache !== nothing && (item = resolve_todo(item, state, flag))
handle_single_case!(ir, idx, stmt, item, todo, state.params, true)
return nothing
end
item = analyze_method!(match, argtypes, flag, state)
end
item = analyze_method!(match, argtypes, flag, state)
handle_single_case!(ir, idx, stmt, item, todo, state.params, true)
return nothing
end
Expand Down Expand Up @@ -1245,18 +1241,28 @@ function handle_const_call!(
for match in meth
j += 1
result = results[j]
if result === false
# Inference determined that this call is guaranteed to throw.
# Do not inline.
fully_covered = false
continue
end
if isa(result, ConstResult)
case = const_result_item(result, state)
if !isdefined(result, :result) || !is_inlineable_constant(result.result)
case = compileable_specialization(state.et, result.mi, EFFECTS_TOTAL)
else
case = ConstantCase(quoted(result.result))
end
signature_union = Union{signature_union, result.mi.specTypes}
push!(cases, InliningCase(result.mi.specTypes, case))
continue
elseif isa(result, InferenceResult)
signature_union = Union{signature_union, result.linfo.specTypes}
fully_covered &= handle_inf_result!(result, argtypes, flag, state, cases)
else
@assert result === nothing
end
if result === nothing
signature_union = Union{signature_union, match.spec_types}
fully_covered &= handle_match!(match, argtypes, flag, state, cases)
else
signature_union = Union{signature_union, result.linfo.specTypes}
fully_covered &= handle_const_result!(result, argtypes, flag, state, cases)
end
end
end
Expand Down Expand Up @@ -1290,7 +1296,7 @@ function handle_match!(
return true
end

function handle_inf_result!(
function handle_const_result!(
result::InferenceResult, argtypes::Vector{Any}, flag::UInt8, state::InliningState,
cases::Vector{InliningCase})
(; mi) = item = InliningTodo(result, argtypes)
Expand All @@ -1303,14 +1309,6 @@ function handle_inf_result!(
return true
end

function const_result_item(result::ConstResult, state::InliningState)
if !isdefined(result, :result) || !is_inlineable_constant(result.result)
return compileable_specialization(state.et, result.mi, EFFECTS_TOTAL)
else
return ConstantCase(quoted(result.result))
end
end

function handle_cases!(ir::IRCode, idx::Int, stmt::Expr, @nospecialize(atype),
cases::Vector{InliningCase}, fully_covered::Bool, todo::Vector{Pair{Int, Any}},
params::OptimizationParams)
Expand Down Expand Up @@ -1377,11 +1375,7 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState)
ir, idx, stmt, result, flag,
sig, state, todo)
else
if isa(result, ConstResult)
item = const_result_item(result, state)
else
item = analyze_method!(info.match, sig.argtypes, flag, state)
end
item = analyze_method!(info.match, sig.argtypes, flag, state)
handle_single_case!(ir, idx, stmt, item, todo, state.params)
end
continue
Expand Down
15 changes: 8 additions & 7 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,9 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState)
return true
end

function CodeInstance(
result::InferenceResult, @nospecialize(inferred_result), valid_worlds::WorldRange)
function CodeInstance(result::InferenceResult, @nospecialize(inferred_result),
valid_worlds::WorldRange, effects::Effects, ipo_effects::Effects,
relocatability::UInt8)
local const_flags::Int32
result_type = result.result
@assert !(result_type isa LimitedAccuracy)
Expand Down Expand Up @@ -308,13 +309,10 @@ function CodeInstance(
const_flags = 0x00
end
end
relocatability = isa(inferred_result, Vector{UInt8}) ? inferred_result[end] : UInt8(0)
return CodeInstance(result.linfo,
widenconst(result_type), rettype_const, inferred_result,
const_flags, first(valid_worlds), last(valid_worlds),
# TODO: Actually do something with non-IPO effects
encode_effects(result.ipo_effects), encode_effects(result.ipo_effects),
relocatability)
encode_effects(effects), encode_effects(ipo_effects), relocatability)
end

# For the NativeInterpreter, we don't need to do an actual cache query to know
Expand Down Expand Up @@ -388,7 +386,10 @@ function cache_result!(interp::AbstractInterpreter, result::InferenceResult)
# TODO: also don't store inferred code if we've previously decided to interpret this function
if !already_inferred
inferred_result = transform_result_for_cache(interp, linfo, valid_worlds, result.src)
code_cache(interp)[linfo] = CodeInstance(result, inferred_result, valid_worlds)
relocatability = isa(inferred_result, Vector{UInt8}) ? inferred_result[end] : UInt8(0)
code_cache(interp)[linfo] = CodeInstance(result, inferred_result, valid_worlds,
# TODO: Actually do something with non-IPO effects
result.ipo_effects, result.ipo_effects, relocatability)
end
unlock_mi_inference(interp, linfo)
nothing
Expand Down
7 changes: 3 additions & 4 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -378,10 +378,9 @@ end
`@assume_effects` overrides the compiler's effect modeling for the given method.
`ex` must be a method definition or `@ccall` expression.
!!! warning
Improper use of this macro causes undefined behavior (including crashes,
incorrect answers, or other hard to track bugs). Use with care and only if
absolutely required.
WARNING: Improper use of this macro causes undefined behavior (including crashes,
incorrect answers, or other hard to track bugs). Use with care and only if absolutely
required.
In general, each `setting` value makes an assertion about the behavior of the
function, without requiring the compiler to prove that this behavior is indeed
Expand Down
71 changes: 27 additions & 44 deletions test/compiler/inline.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ using Test
using Base.Meta
using Core: ReturnNode

include(normpath(@__DIR__, "irutils.jl"))

"""
Helper to walk the AST and call a function on every node.
"""
Expand Down Expand Up @@ -152,6 +150,19 @@ end
@test !any(x -> x isa Expr && x.head === :invoke, src.code)
end

function fully_eliminated(f, args)
@nospecialize f args
let code = code_typed(f, args)[1][1].code
return length(code) == 1 && isa(code[1], ReturnNode)
end
end
function fully_eliminated(f, args, retval)
@nospecialize f args
let code = code_typed(f, args)[1][1].code
return length(code) == 1 && isa(code[1], ReturnNode) && code[1].val == retval
end
end

# check that ismutabletype(type) can be fully eliminated
f_mutable_nothrow(s::String) = Val{typeof(s).name.flags}
@test fully_eliminated(f_mutable_nothrow, (String,))
Expand Down Expand Up @@ -235,7 +246,7 @@ function f_subtype()
T = SomeArbitraryStruct
T <: Bool
end
@test fully_eliminated(f_subtype, Tuple{}; retval=false)
@test fully_eliminated(f_subtype, Tuple{}, false)

# check that pointerref gets deleted if unused
f_pointerref(T::Type{S}) where S = Val(length(T.parameters))
Expand All @@ -259,7 +270,7 @@ function foo_apply_apply_type_svec()
B = Tuple{Float32, Float32}
Core.apply_type(A..., B.types...)
end
@test fully_eliminated(foo_apply_apply_type_svec, Tuple{}; retval=NTuple{3, Float32})
@test fully_eliminated(foo_apply_apply_type_svec, Tuple{}, NTuple{3, Float32})

# The that inlining doesn't drop ambiguity errors (#30118)
c30118(::Tuple{Ref{<:Type}, Vararg}) = nothing
Expand All @@ -273,7 +284,7 @@ b30118(x...) = c30118(x)
f34900(x::Int, y) = x
f34900(x, y::Int) = y
f34900(x::Int, y::Int) = invoke(f34900, Tuple{Int, Any}, x, y)
@test fully_eliminated(f34900, Tuple{Int, Int}; retval=Core.Argument(2))
@test fully_eliminated(f34900, Tuple{Int, Int}, Core.Argument(2))

@testset "check jl_ir_flag_inlineable for inline macro" begin
@test ccall(:jl_ir_flag_inlineable, Bool, (Any,), first(methods(@inline x -> x)).source)
Expand Down Expand Up @@ -313,7 +324,10 @@ struct NonIsBitsDims
dims::NTuple{N, Int} where N
end
NonIsBitsDims() = NonIsBitsDims(())
@test fully_eliminated(NonIsBitsDims, (); retval=QuoteNode(NonIsBitsDims()))
let ci = code_typed(NonIsBitsDims, Tuple{})[1].first
@test length(ci.code) == 1 && isa(ci.code[1], ReturnNode) &&
ci.code[1].val.value == NonIsBitsDims()
end

struct NonIsBitsDimsUndef
dims::NTuple{N, Int} where N
Expand Down Expand Up @@ -909,7 +923,7 @@ end
g() = Core.get_binding_type($m, :y)
end

@test fully_eliminated(m.f, Tuple{}; retval=Int)
@test fully_eliminated(m.f, Tuple{}, Int)
src = code_typed(m.g, ())[][1]
@test count(iscall((src, Core.get_binding_type)), src.code) == 1
@test m.g() === Any
Expand Down Expand Up @@ -948,48 +962,17 @@ end
@test fully_eliminated(f_sin_perf, Tuple{})

# Test that we inline the constructor of something that is not const-inlineable
const THE_REF_NULL = Ref{Int}()
const THE_REF = Ref{Int}(0)
struct FooTheRef
x::Ref
FooTheRef(v) = new(v === nothing ? THE_REF_NULL : THE_REF)
end
let src = code_typed1() do
FooTheRef(nothing)
end
@test count(isnew, src.code) == 1
end
let src = code_typed1() do
FooTheRef(0)
end
@test count(isnew, src.code) == 1
FooTheRef() = new(THE_REF)
end
let src = code_typed1() do
Base.@invoke FooTheRef(nothing::Any)
end
@test count(isnew, src.code) == 1
end
let src = code_typed1() do
Base.@invoke FooTheRef(0::Any)
end
@test count(isnew, src.code) == 1
end
@test fully_eliminated() do
FooTheRef(nothing)
nothing
end
@test fully_eliminated() do
FooTheRef(0)
nothing
end
@test fully_eliminated() do
Base.@invoke FooTheRef(nothing::Any)
nothing
end
@test fully_eliminated() do
Base.@invoke FooTheRef(0::Any)
nothing
f_make_the_ref() = FooTheRef()
f_make_the_ref_but_dont_return_it() = (FooTheRef(); nothing)
let src = code_typed1(f_make_the_ref, ())
@test count(x->isexpr(x, :new), src.code) == 1
end
@test fully_eliminated(f_make_the_ref_but_dont_return_it, Tuple{})

# Test that the Core._apply_iterate bail path taints effects
function f_apply_bail(f)
Expand Down
49 changes: 46 additions & 3 deletions test/compiler/irpasses.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,31 @@ using Test
using Base.Meta
using Core: PhiNode, SSAValue, GotoNode, PiNode, QuoteNode, ReturnNode, GotoIfNot

include(normpath(@__DIR__, "irutils.jl"))
# utilities
# =========

import Core.Compiler: argextype, singleton_type

argextype(@nospecialize args...) = argextype(args..., Any[])
code_typed1(args...; kwargs...) = first(only(code_typed(args...; kwargs...)))::Core.CodeInfo
get_code(args...; kwargs...) = code_typed1(args...; kwargs...).code

# check if `x` is a statement with a given `head`
isnew(@nospecialize x) = Meta.isexpr(x, :new)

# check if `x` is a dynamic call of a given function
iscall(y) = @nospecialize(x) -> iscall(y, x)
function iscall((src, f)::Tuple{Core.CodeInfo,Base.Callable}, @nospecialize(x))
return iscall(x) do @nospecialize x
singleton_type(argextype(x, src)) === f
end
end
iscall(pred::Base.Callable, @nospecialize(x)) = Meta.isexpr(x, :call) && pred(x.args[1])

# check if `x` is a statically-resolved call of a function whose name is `sym`
isinvoke(y) = @nospecialize(x) -> isinvoke(y, x)
isinvoke(sym::Symbol, @nospecialize(x)) = isinvoke(mi->mi.def.name===sym, x)
isinvoke(pred::Function, @nospecialize(x)) = Meta.isexpr(x, :invoke) && pred(x.args[1]::Core.MethodInstance)

# domsort
# =======
Expand Down Expand Up @@ -449,7 +473,9 @@ struct FooPartial
global f_partial
f_partial(x) = new(x, 2).x
end
@test fully_eliminated(f_partial, Tuple{Float64})
let ci = code_typed(f_partial, Tuple{Float64})[1].first
@test length(ci.code) == 1 && isa(ci.code[1], ReturnNode)
end

# A SSAValue after the compaction line
let m = Meta.@lower 1 + 1
Expand Down Expand Up @@ -631,7 +657,11 @@ function no_op_refint(r)
r[]
return
end
@test fully_eliminated(no_op_refint,Tuple{Base.RefValue{Int}}; retval=nothing)
let code = code_typed(no_op_refint,Tuple{Base.RefValue{Int}})[1].first.code
@test length(code) == 1
@test isa(code[1], Core.ReturnNode)
@test code[1].val === nothing
end

# check getfield elim handling of GlobalRef
const _some_coeffs = (1,[2],3,4)
Expand Down Expand Up @@ -743,6 +773,19 @@ end
# test `stmt_effect_free` and DCE
# ===============================

function fully_eliminated(f, args)
@nospecialize f args
let code = code_typed(f, args)[1][1].code
return length(code) == 1 && isa(code[1], ReturnNode)
end
end
function fully_eliminated(f, args, retval)
@nospecialize f args
let code = code_typed(f, args)[1][1].code
return length(code) == 1 && isa(code[1], ReturnNode) && code[1].val == retval
end
end

let # effect-freeness computation for array allocation

# should eliminate dead allocations
Expand Down
Loading

0 comments on commit d255809

Please sign in to comment.