Skip to content

Commit

Permalink
effects: complete edge effect overrides
Browse files Browse the repository at this point in the history
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
```
  • Loading branch information
aviatesk committed Mar 11, 2022
1 parent e90dec9 commit d8fb9d0
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 16 deletions.
13 changes: 13 additions & 0 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
74 changes: 58 additions & 16 deletions base/compiler/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand All @@ -65,24 +98,32 @@ 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),
tristate_merge(
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
Expand All @@ -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
Expand Down
6 changes: 6 additions & 0 deletions test/compiler/inline.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit d8fb9d0

Please sign in to comment.