From f894fba50156b92166c570eab4a91127fd3b1a0e Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki Date: Fri, 11 Mar 2022 20:04:10 +0900 Subject: [PATCH] effects: complements #43852, implement effect override mechanisms The PR #43852 missed to implement the mechanism to override analyzed effects with effect settings annotated by `Base.@assume_effects`. This commits adds such an mechanism within `finish(::InferenceState, ::AbstractInterpreter)`, just after inference analyzed frame effect. Now we can do something like: ```julia Base.@assume_effects :consistent :effect_free :terminates_globally consteval( f, args...; kwargs...) = f(args...; kwargs...) const ___CONST_DICT___ = Dict{Any,Any}(:a => 1, :b => 2) @test fully_eliminated() do consteval(getindex, ___CONST_DICT___, :a) end ``` --- base/compiler/abstractinterpretation.jl | 7 --- base/compiler/inferencestate.jl | 15 +++++++ base/compiler/typeinfer.jl | 28 ++++++++---- base/compiler/types.jl | 60 ++++++++++++++++++------- test/compiler/inline.jl | 7 +++ 5 files changed, 85 insertions(+), 32 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 62d92b766c6ca6..7984f8d5239468 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -620,13 +620,6 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp return MethodCallResult(rt, edgecycle, edgelimited, edge, edge_effects) end -is_effect_overrided(sv::InferenceState, effect::Symbol) = is_effect_overrided(sv.linfo, effect) -function is_effect_overrided(linfo::MethodInstance, effect::Symbol) - def = linfo.def - return isa(def, Method) && is_effect_overrided(def, effect) -end -is_effect_overrided(method::Method, effect::Symbol) = getfield(decode_effects_override(method.purity), effect) - # keeps result and context information of abstract method call, will be used by succeeding constant-propagation struct MethodCallResult rt diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 928811dd63a3b7..74e60bedc199fb 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -141,8 +141,23 @@ mutable struct InferenceState return frame end end + Effects(state::InferenceState) = state.ipo_effects +function tristate_merge!(caller::InferenceState, effects::Effects) + caller.ipo_effects = tristate_merge(caller.ipo_effects, effects) +end +tristate_merge!(caller::InferenceState, callee::InferenceState) = + tristate_merge!(caller, Effects(callee)) + +is_effect_overrided(sv::InferenceState, effect::Symbol) = is_effect_overrided(sv.linfo, effect) +function is_effect_overrided(linfo::MethodInstance, effect::Symbol) + def = linfo.def + return isa(def, Method) && is_effect_overrided(def, effect) +end +is_effect_overrided(method::Method, effect::Symbol) = is_effect_overrided(decode_effects_override(method.purity), effect) +is_effect_overrided(override::EffectsOverride, effect::Symbol) = getfield(override, effect) + function any_inbounds(code::Vector{Any}) for i=1:length(code) stmt = code[i] diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 4015b7c00bf0d6..5bdaa0b5d3c0fe 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -494,7 +494,25 @@ function finish(me::InferenceState, interp::AbstractInterpreter) end me.result.valid_worlds = me.valid_worlds me.result.result = me.bestguess - me.result.ipo_effects = rt_adjust_effects(me.bestguess, me.ipo_effects) + ipo_effects = rt_adjust_effects(me.bestguess, me.ipo_effects) + # override the analyzed effects using manually annotated effect settings + def = me.linfo.def + if isa(def, Method) + override = decode_effects_override(def.purity) + if is_effect_overrided(override, :consistent) + ipo_effects = Effects(ipo_effects; consistent=ALWAYS_TRUE) + end + if is_effect_overrided(override, :effect_free) + ipo_effects = Effects(ipo_effects; effect_free=ALWAYS_TRUE) + end + if is_effect_overrided(override, :nothrow) + ipo_effects = Effects(ipo_effects; nothrow=ALWAYS_TRUE) + end + if is_effect_overrided(override, :terminates_globally) + ipo_effects = Effects(ipo_effects; terminates=ALWAYS_TRUE) + end + end + me.result.ipo_effects = ipo_effects validate_code_in_debug_mode(me.linfo, me.src, "inferred") nothing end @@ -797,14 +815,6 @@ end generating_sysimg() = ccall(:jl_generating_output, Cint, ()) != 0 && JLOptions().incremental == 0 -function tristate_merge!(caller::InferenceState, callee::Effects) - caller.ipo_effects = tristate_merge(caller.ipo_effects, callee) -end - -function tristate_merge!(caller::InferenceState, callee::InferenceState) - tristate_merge!(caller, Effects(callee)) -end - ipo_effects(code::CodeInstance) = decode_effects(code.ipo_purity_bits) # compute (and cache) an inferred AST and return the current best estimate of the result type diff --git a/base/compiler/types.jl b/base/compiler/types.jl index 6d4a650470237c..65ce341dd55e10 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -42,14 +42,33 @@ 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 is_total_or_error(effects::Effects) = effects.consistent === ALWAYS_TRUE && effects.effect_free === ALWAYS_TRUE && @@ -65,15 +84,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 +109,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 +127,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 7619d4e8a03085..fa4425893767c8 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -1088,6 +1088,13 @@ 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...; kwargs...) = f(args...; kwargs...) +@test fully_eliminated() do + consteval(getindex, ___CONST_DICT___, :a) +end + global x44200::Int = 0 function f44200() global x = 0