Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

effects: complements #43852, implement effect override mechanisms #44561

Merged
merged 1 commit into from
Mar 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 6 additions & 11 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -606,10 +606,12 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp
if edge === nothing
edgecycle = edgelimited = true
end
if is_effect_overrided(sv, :terminates_globally)
# we look for the termination effect override here as well, since the :terminates effect
# may have been tainted due to recursion at this point even if it's overridden
if is_effect_overridden(sv, :terminates_globally)
# this frame is known to terminate
edge_effects = Effects(edge_effects, terminates=ALWAYS_TRUE)
elseif is_effect_overrided(method, :terminates_globally)
elseif is_effect_overridden(method, :terminates_globally)
# this edge is known to terminate
edge_effects = Effects(edge_effects, terminates=ALWAYS_TRUE)
elseif edgecycle
Expand All @@ -620,13 +622,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
Expand Down Expand Up @@ -2106,9 +2101,9 @@ end

function handle_control_backedge!(frame::InferenceState, from::Int, to::Int)
if from > to
if is_effect_overrided(frame, :terminates_globally)
if is_effect_overridden(frame, :terminates_globally)
# this frame is known to terminate
elseif is_effect_overrided(frame, :terminates_locally)
elseif is_effect_overridden(frame, :terminates_locally)
# this backedge is known to terminate
else
tristate_merge!(frame, Effects(EFFECTS_TOTAL, terminates=TRISTATE_UNKNOWN))
Expand Down
15 changes: 15 additions & 0 deletions base/compiler/inferencestate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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_overridden(sv::InferenceState, effect::Symbol) = is_effect_overridden(sv.linfo, effect)
function is_effect_overridden(linfo::MethodInstance, effect::Symbol)
def = linfo.def
return isa(def, Method) && is_effect_overridden(def, effect)
end
is_effect_overridden(method::Method, effect::Symbol) = is_effect_overridden(decode_effects_override(method.purity), effect)
is_effect_overridden(override::EffectsOverride, effect::Symbol) = getfield(override, effect)

function any_inbounds(code::Vector{Any})
for i=1:length(code)
stmt = code[i]
Expand Down
28 changes: 19 additions & 9 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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_overridden(override, :consistent)
ipo_effects = Effects(ipo_effects; consistent=ALWAYS_TRUE)
end
if is_effect_overridden(override, :effect_free)
ipo_effects = Effects(ipo_effects; effect_free=ALWAYS_TRUE)
end
if is_effect_overridden(override, :nothrow)
ipo_effects = Effects(ipo_effects; nothrow=ALWAYS_TRUE)
end
if is_effect_overridden(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
Expand Down Expand Up @@ -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
Expand Down
60 changes: 44 additions & 16 deletions base/compiler/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand All @@ -65,24 +84,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 +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
Expand Down
7 changes: 7 additions & 0 deletions test/compiler/inline.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down