From d8fb9d091b238ab5096d2fae3a3678b54b3608c0 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki Date: Fri, 11 Mar 2022 20:04:10 +0900 Subject: [PATCH] effects: complete edge effect overrides While working on #44515, I found we missed to override edge effect with effects set by `Base.@assume_effects`. This commit adds edge effect override mechanism for `:consistent`/`:effect_free`/`:nothrow` settings in the same way as we did for `:terminates_globally` in #44106. Now we can do something like: ```julia const ___CONST_DICT___ = Dict{Any,Any}(:a => 1, :b => 2) Base.@assume_effects :consistent :effect_free :terminates_globally consteval(f, args...) = f(args...) @test fully_eliminated() do consteval(getindex, ___CONST_DICT___, :a) end ``` --- base/compiler/abstractinterpretation.jl | 13 +++++ base/compiler/types.jl | 74 +++++++++++++++++++------ test/compiler/inline.jl | 6 ++ 3 files changed, 77 insertions(+), 16 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 62d92b766c6ca..2db180c4004d1 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -606,6 +606,9 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp if edge === nothing edgecycle = edgelimited = true end + edge_effects = override_edge_effects(edge_effects, sv, method, :consistent) + edge_effects = override_edge_effects(edge_effects, sv, method, :effect_free) + edge_effects = override_edge_effects(edge_effects, sv, method, :nothrow) if is_effect_overrided(sv, :terminates_globally) # this frame is known to terminate edge_effects = Effects(edge_effects, terminates=ALWAYS_TRUE) @@ -627,6 +630,16 @@ function is_effect_overrided(linfo::MethodInstance, effect::Symbol) end is_effect_overrided(method::Method, effect::Symbol) = getfield(decode_effects_override(method.purity), effect) +function override_edge_effects(edge_effects::Effects, + sv::InferenceState, method::Method, effect::Symbol) + if is_effect_overrided(sv, effect) + return override_effect(edge_effects, effect, ALWAYS_TRUE) + elseif is_effect_overrided(method, :consistent) + return override_effect(edge_effects, effect, ALWAYS_TRUE) + end + return edge_effects +end + # keeps result and context information of abstract method call, will be used by succeeding constant-propagation struct MethodCallResult rt diff --git a/base/compiler/types.jl b/base/compiler/types.jl index 6d4a650470237..008b5e1275014 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -42,14 +42,47 @@ struct Effects # :consistent before caching. We may want to track it in the future. inbounds_taints_consistency::Bool end -Effects(consistent::TriState, effect_free::TriState, nothrow::TriState, terminates::TriState) = - Effects(consistent, effect_free, nothrow, terminates, false) +function Effects( + consistent::TriState, + effect_free::TriState, + nothrow::TriState, + terminates::TriState) + return Effects( + consistent, + effect_free, + nothrow, + terminates, + false) +end Effects() = Effects(TRISTATE_UNKNOWN, TRISTATE_UNKNOWN, TRISTATE_UNKNOWN, TRISTATE_UNKNOWN) -Effects(e::Effects; consistent::TriState=e.consistent, - effect_free::TriState = e.effect_free, nothrow::TriState=e.nothrow, terminates::TriState=e.terminates, - inbounds_taints_consistency::Bool = e.inbounds_taints_consistency) = - Effects(consistent, effect_free, nothrow, terminates, inbounds_taints_consistency) +function Effects(e::Effects; + consistent::TriState = e.consistent, + effect_free::TriState = e.effect_free, + nothrow::TriState = e.nothrow, + terminates::TriState = e.terminates, + inbounds_taints_consistency::Bool = e.inbounds_taints_consistency) + return Effects( + consistent, + effect_free, + nothrow, + terminates, + inbounds_taints_consistency) +end + +function override_effect(e::Effects, sym::Symbol, val::Union{TriState,Bool}) + if sym === :consistent + return Effects(e, consistent=val::TriState) + elseif sym === :effect_free + return Effects(e, effect_free=val::TriState) + elseif sym === :nothrow + return Effects(e, nothrow=val::TriState) + elseif sym === :terminates + return Effects(e, terminates=val::TriState) + else + return Effects(e, inbounds_taints_consistency=val::Bool) + end +end is_total_or_error(effects::Effects) = effects.consistent === ALWAYS_TRUE && effects.effect_free === ALWAYS_TRUE && @@ -65,15 +98,24 @@ is_removable_if_unused(effects::Effects) = const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE) -encode_effects(e::Effects) = e.consistent.state | (e.effect_free.state << 2) | (e.nothrow.state << 4) | (e.terminates.state << 6) -decode_effects(e::UInt8) = - Effects(TriState(e & 0x3), +function encode_effects(e::Effects) + return e.consistent.state | + (e.effect_free.state << 2) | + (e.nothrow.state << 4) | + (e.terminates.state << 6) +end +function decode_effects(e::UInt8) + return Effects( + TriState(e & 0x3), TriState((e >> 2) & 0x3), TriState((e >> 4) & 0x3), - TriState((e >> 6) & 0x3), false) + TriState((e >> 6) & 0x3), + false) +end function tristate_merge(old::Effects, new::Effects) - Effects(tristate_merge( + return Effects( + tristate_merge( old.consistent, new.consistent), tristate_merge( old.effect_free, new.effect_free), @@ -81,8 +123,7 @@ function tristate_merge(old::Effects, new::Effects) old.nothrow, new.nothrow), tristate_merge( old.terminates, new.terminates), - old.inbounds_taints_consistency || - new.inbounds_taints_consistency) + old.inbounds_taints_consistency | new.inbounds_taints_consistency) end struct EffectsOverride @@ -100,16 +141,17 @@ function encode_effects_override(eo::EffectsOverride) eo.nothrow && (e |= 0x04) eo.terminates_globally && (e |= 0x08) eo.terminates_locally && (e |= 0x10) - e + return e end -decode_effects_override(e::UInt8) = - EffectsOverride( +function decode_effects_override(e::UInt8) + return EffectsOverride( (e & 0x01) != 0x00, (e & 0x02) != 0x00, (e & 0x04) != 0x00, (e & 0x08) != 0x00, (e & 0x10) != 0x00) +end """ InferenceResult diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 7619d4e8a0308..8c379f56f9706 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -1088,6 +1088,12 @@ recur_termination22(x) = x * recur_termination21(x-1) recur_termination21(12) + recur_termination22(12) end +const ___CONST_DICT___ = Dict{Any,Any}(:a => 1, :b => 2) +Base.@assume_effects :consistent :effect_free :terminates_globally consteval(f, args...) = f(args...) +@test fully_eliminated() do + consteval(getindex, ___CONST_DICT___, :a) +end + global x44200::Int = 0 function f44200() global x = 0