Skip to content

Commit

Permalink
extend :nonoverlayed effect bit and add new :consistent_overlay o…
Browse files Browse the repository at this point in the history
…verride
  • Loading branch information
aviatesk committed May 1, 2024
1 parent 68c8e5e commit 146a43a
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 84 deletions.
2 changes: 1 addition & 1 deletion base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ macro _foldable_meta()
#=:inaccessiblememonly=#true,
#=:noub=#true,
#=:noub_if_noinbounds=#false,
#=:nonoverlayed=#false))
#=:consistent_overlay=#false))
end

macro inline() Expr(:meta, :inline) end
Expand Down
17 changes: 12 additions & 5 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ function add_call_backedges!(interp::AbstractInterpreter, @nospecialize(rettype)
# ignore the `:nonoverlayed` property if `interp` doesn't use overlayed method table
# since it will never be tainted anyway
if !isoverlayed(method_table(interp))
all_effects = Effects(all_effects; nonoverlayed=false)
all_effects = Effects(all_effects; nonoverlayed=ALWAYS_FALSE)
end
all_effects === Effects() && return nothing
end
Expand Down Expand Up @@ -898,7 +898,15 @@ function concrete_eval_eligible(interp::AbstractInterpreter,
mi = result.edge
if mi !== nothing && is_foldable(effects)
if f !== nothing && is_all_const_arg(arginfo, #=start=#2)
if is_nonoverlayed(interp) || is_nonoverlayed(effects)
if (is_nonoverlayed(interp) || is_nonoverlayed(effects) ||
# Even if overlay methods are involved, when `:consistent_overlay` is
# explicitly applied, we can still perform concrete evaluation using the
# original methods for executing them.
# While there's a chance that the non-overlayed counterparts may raise
# non-egal exceptions, it will not impact the compilation validity, since:
# - the results of the concrete evaluation will not be inlined
# - the exception types from the concrete evaluation will not be propagated
is_consistent_overlay(effects))
return :concrete_eval
end
# disable concrete-evaluation if this function call is tainted by some overlayed
Expand Down Expand Up @@ -2825,9 +2833,8 @@ function override_effects(effects::Effects, override::EffectsOverride)
notaskstate = override.notaskstate ? true : effects.notaskstate,
inaccessiblememonly = override.inaccessiblememonly ? ALWAYS_TRUE : effects.inaccessiblememonly,
noub = override.noub ? ALWAYS_TRUE :
override.noub_if_noinbounds && effects.noub !== ALWAYS_TRUE ? NOUB_IF_NOINBOUNDS :
effects.noub,
nonoverlayed = override.nonoverlayed ? true : effects.nonoverlayed)
(override.noub_if_noinbounds && effects.noub !== ALWAYS_TRUE) ? NOUB_IF_NOINBOUNDS :
effects.noub)
end

isdefined_globalref(g::GlobalRef) = !iszero(ccall(:jl_globalref_boundp, Cint, (Any,), g))
Expand Down
6 changes: 3 additions & 3 deletions base/compiler/compiler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ struct EffectsOverride
inaccessiblememonly::Bool
noub::Bool
noub_if_noinbounds::Bool
nonoverlayed::Bool
consistent_overlay::Bool
end
function EffectsOverride(
override::EffectsOverride =
Expand All @@ -61,7 +61,7 @@ function EffectsOverride(
inaccessiblememonly::Bool = override.inaccessiblememonly,
noub::Bool = override.noub,
noub_if_noinbounds::Bool = override.noub_if_noinbounds,
nonoverlayed::Bool = override.nonoverlayed)
consistent_overlay::Bool = override.consistent_overlay)
return EffectsOverride(
consistent,
effect_free,
Expand All @@ -72,7 +72,7 @@ function EffectsOverride(
inaccessiblememonly,
noub,
noub_if_noinbounds,
nonoverlayed)
consistent_overlay)
end
const NUM_EFFECTS_OVERRIDES = 10 # sync with julia.h

Expand Down
57 changes: 36 additions & 21 deletions base/compiler/effects.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,21 @@ following meanings:
except that it may access or modify mutable memory pointed to by its call arguments.
This may later be refined to `ALWAYS_TRUE` in a case when call arguments are known to be immutable.
This state corresponds to LLVM's `inaccessiblemem_or_argmemonly` function attribute.
- `noub::UInt8`: indicates that the method will not execute any undefined behavior (for any input).
Note that undefined behavior may technically cause the method to violate any other effect
assertions (such as `:consistent` or `:effect_free`) as well, but we do not model this,
and they assume the absence of undefined behavior.
* `ALWAYS_TRUE`: this method is guaranteed to not execute any undefined behavior.
- `noub::UInt8`:
* `ALWAYS_TRUE`: this method is guaranteed to not execute any undefined behavior (for any input).
* `ALWAYS_FALSE`: this method may execute undefined behavior.
* `NOUB_IF_NOINBOUNDS`: this method is guaranteed to not execute any undefined behavior
if the caller does not set nor propagate the `@inbounds` context.
- `nonoverlayed::Bool`: indicates that any methods that may be called within this method
are not defined in an [overlayed method table](@ref OverlayMethodTable).
Note that undefined behavior may technically cause the method to violate any other effect
assertions (such as `:consistent` or `:effect_free`) as well, but we do not model this,
and they assume the absence of undefined behavior.
- `nonoverlayed::UInt8`:
* `ALWAYS_TRUE`: this method is guaranteed to not invoke any methods that defined in an
[overlayed method table](@ref OverlayMethodTable).
* `CONSISTENT_OVERLAY`: this method may invoke overlayed methods, but all such overlayed
methods are `:consistent` with their non-overlayed original counterparts
(see [`Base.@assume_effects`](@ref) for the exact definition of `:consistenct`-cy).
* `ALWAYS_FALSE`: this method may invoke overlayed methods.
Note that the representations above are just internal implementation details and thus likely
to change in the future. See [`Base.@assume_effects`](@ref) for more detailed explanation
Expand Down Expand Up @@ -94,8 +99,10 @@ The output represents the state of different effect properties in the following
- `+u` (green): `true`
- `-u` (red): `false`
- `?u` (yellow): `NOUB_IF_NOINBOUNDS`
Additionally, if the `nonoverlayed` property is false, a red prime symbol (′) is displayed after the tuple.
8. `:nonoverlayed` (`o`):
- `+o` (green): `ALWAYS_TRUE`
- `-o` (red): `ALWAYS_FALSE`
- `?o` (yellow): `CONSISTENT_OVERLAY`
"""
struct Effects
consistent::UInt8
Expand All @@ -105,7 +112,7 @@ struct Effects
notaskstate::Bool
inaccessiblememonly::UInt8
noub::UInt8
nonoverlayed::Bool
nonoverlayed::UInt8
function Effects(
consistent::UInt8,
effect_free::UInt8,
Expand All @@ -114,7 +121,7 @@ struct Effects
notaskstate::Bool,
inaccessiblememonly::UInt8,
noub::UInt8,
nonoverlayed::Bool)
nonoverlayed::UInt8)
return new(
consistent,
effect_free,
Expand Down Expand Up @@ -150,10 +157,13 @@ const INACCESSIBLEMEM_OR_ARGMEMONLY = 0x01 << 1
# :noub bits
const NOUB_IF_NOINBOUNDS = 0x01 << 1

const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, true)
const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, true)
const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, true) # unknown mostly, but it's not overlayed at least (e.g. it's not a call)
const _EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, false) # unknown really
# :nonoverlayed bits
const CONSISTENT_OVERLAY = 0x01 << 1

const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE)
const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE)
const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_TRUE) # unknown mostly, but it's not overlayed at least (e.g. it's not a call)
const _EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_FALSE) # unknown really

function Effects(effects::Effects = _EFFECTS_UNKNOWN;
consistent::UInt8 = effects.consistent,
Expand All @@ -163,7 +173,7 @@ function Effects(effects::Effects = _EFFECTS_UNKNOWN;
notaskstate::Bool = effects.notaskstate,
inaccessiblememonly::UInt8 = effects.inaccessiblememonly,
noub::UInt8 = effects.noub,
nonoverlayed::Bool = effects.nonoverlayed)
nonoverlayed::UInt8 = effects.nonoverlayed)
return Effects(
consistent,
effect_free,
Expand Down Expand Up @@ -229,8 +239,11 @@ function is_better_effects(new::Effects, old::Effects)
elseif new.noub != old.noub
return false
end
if new.nonoverlayed
any_improved |= !old.nonoverlayed
if new.nonoverlayed == ALWAYS_TRUE
any_improved |= old.nonoverlayed != ALWAYS_TRUE
elseif new.nonoverlayed == CONSISTENT_OVERLAY
old.nonoverlayed == ALWAYS_TRUE && return false
any_improved |= old.nonoverlayed != CONSISTENT_OVERLAY
elseif new.nonoverlayed != old.nonoverlayed
return false
end
Expand Down Expand Up @@ -265,7 +278,7 @@ is_notaskstate(effects::Effects) = effects.notaskstate
is_inaccessiblememonly(effects::Effects) = effects.inaccessiblememonly === ALWAYS_TRUE
is_noub(effects::Effects) = effects.noub === ALWAYS_TRUE
is_noub_if_noinbounds(effects::Effects) = effects.noub === NOUB_IF_NOINBOUNDS
is_nonoverlayed(effects::Effects) = effects.nonoverlayed
is_nonoverlayed(effects::Effects) = effects.nonoverlayed === ALWAYS_TRUE

# implies `is_notaskstate` & `is_inaccessiblememonly`, but not explicitly checked here
is_foldable(effects::Effects) =
Expand Down Expand Up @@ -295,6 +308,8 @@ is_effect_free_if_inaccessiblememonly(effects::Effects) = !iszero(effects.effect

is_inaccessiblemem_or_argmemonly(effects::Effects) = effects.inaccessiblememonly === INACCESSIBLEMEM_OR_ARGMEMONLY

is_consistent_overlay(effects::Effects) = effects.nonoverlayed === CONSISTENT_OVERLAY

function encode_effects(e::Effects)
return ((e.consistent % UInt32) << 0) |
((e.effect_free % UInt32) << 3) |
Expand All @@ -315,7 +330,7 @@ function decode_effects(e::UInt32)
_Bool((e >> 7) & 0x01),
UInt8((e >> 8) & 0x03),
UInt8((e >> 10) & 0x03),
_Bool((e >> 12) & 0x01))
UInt8((e >> 12) & 0x03))
end

function encode_effects_override(eo::EffectsOverride)
Expand All @@ -329,7 +344,7 @@ function encode_effects_override(eo::EffectsOverride)
eo.inaccessiblememonly && (e |= (0x0001 << 6))
eo.noub && (e |= (0x0001 << 7))
eo.noub_if_noinbounds && (e |= (0x0001 << 8))
eo.nonoverlayed && (e |= (0x0001 << 9))
eo.consistent_overlay && (e |= (0x0001 << 9))
return e
end

Expand Down
5 changes: 4 additions & 1 deletion base/compiler/inferencestate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,10 @@ mutable struct InferenceState
end

if def isa Method
ipo_effects = Effects(ipo_effects; nonoverlayed=is_nonoverlayed(def))
nonoverlayed = is_nonoverlayed(def) ? ALWAYS_TRUE :
is_effect_overridden(def, :consistent_overlay) ? CONSISTENT_OVERLAY :
ALWAYS_FALSE
ipo_effects = Effects(ipo_effects; nonoverlayed)
end

restrict_abstract_call_sites = isa(def, Module)
Expand Down
3 changes: 2 additions & 1 deletion base/compiler/ssair/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1048,8 +1048,9 @@ function Base.show(io::IO, e::Effects)
printstyled(io, effectbits_letter(e, :inaccessiblememonly, 'm'); color=effectbits_color(e, :inaccessiblememonly))
print(io, ',')
printstyled(io, effectbits_letter(e, :noub, 'u'); color=effectbits_color(e, :noub))
print(io, ',')
printstyled(io, effectbits_letter(e, :nonoverlayed, 'o'); color=effectbits_color(e, :nonoverlayed))
print(io, ')')
e.nonoverlayed || printstyled(io, ''; color=:red)
end

@specialize
4 changes: 2 additions & 2 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -448,8 +448,8 @@ function adjust_effects(ipo_effects::Effects, def::Method)
elseif is_effect_overridden(override, :noub_if_noinbounds) && ipo_effects.noub !== ALWAYS_TRUE
ipo_effects = Effects(ipo_effects; noub=NOUB_IF_NOINBOUNDS)
end
if is_effect_overridden(override, :nonoverlayed)
ipo_effects = Effects(ipo_effects; nonoverlayed=true)
if is_effect_overridden(override, :consistent_overlay)
ipo_effects = Effects(ipo_effects; nonoverlayed=CONSISTENT_OVERLAY)
end
return ipo_effects
end
Expand Down
24 changes: 12 additions & 12 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ macro _total_meta()
#=:inaccessiblememonly=#true,
#=:noub=#true,
#=:noub_if_noinbounds=#false,
#=:nonoverlayed=#false))
#=:consistent_overlay=#false))
end
# can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping)
macro _foldable_meta()
Expand All @@ -216,7 +216,7 @@ macro _foldable_meta()
#=:inaccessiblememonly=#true,
#=:noub=#true,
#=:noub_if_noinbounds=#false,
#=:nonoverlayed=#false))
#=:consistent_overlay=#false))
end
# can be used in place of `@assume_effects :terminates_locally` (supposed to be used for bootstrapping)
macro _terminates_locally_meta()
Expand All @@ -230,7 +230,7 @@ macro _terminates_locally_meta()
#=:inaccessiblememonly=#false,
#=:noub=#false,
#=:noub_if_noinbounds=#false,
#=:nonoverlayed=#false))
#=:consistent_overlay=#false))
end
# can be used in place of `@assume_effects :terminates_globally` (supposed to be used for bootstrapping)
macro _terminates_globally_meta()
Expand All @@ -244,7 +244,7 @@ macro _terminates_globally_meta()
#=:inaccessiblememonly=#false,
#=:noub=#false,
#=:noub_if_noinbounds=#false,
#=:nonoverlayed=#false))
#=:consistent_overlay=#false))
end
# can be used in place of `@assume_effects :terminates_globally :notaskstate` (supposed to be used for bootstrapping)
macro _terminates_globally_notaskstate_meta()
Expand All @@ -258,7 +258,7 @@ macro _terminates_globally_notaskstate_meta()
#=:inaccessiblememonly=#false,
#=:noub=#false,
#=:noub_if_noinbounds=#false,
#=:nonoverlayed=#false))
#=:consistent_overlay=#false))
end
# can be used in place of `@assume_effects :terminates_globally :noub` (supposed to be used for bootstrapping)
macro _terminates_globally_noub_meta()
Expand All @@ -272,7 +272,7 @@ macro _terminates_globally_noub_meta()
#=:inaccessiblememonly=#false,
#=:noub=#true,
#=:noub_if_noinbounds=#false,
#=:nonoverlayed=#false))
#=:consistent_overlay=#false))
end
# can be used in place of `@assume_effects :effect_free :terminates_locally` (supposed to be used for bootstrapping)
macro _effect_free_terminates_locally_meta()
Expand All @@ -286,7 +286,7 @@ macro _effect_free_terminates_locally_meta()
#=:inaccessiblememonly=#false,
#=:noub=#false,
#=:noub_if_noinbounds=#false,
#=:nonoverlayed=#false))
#=:consistent_overlay=#false))
end
# can be used in place of `@assume_effects :nothrow :noub` (supposed to be used for bootstrapping)
macro _nothrow_noub_meta()
Expand All @@ -300,7 +300,7 @@ macro _nothrow_noub_meta()
#=:inaccessiblememonly=#false,
#=:noub=#true,
#=:noub_if_noinbounds=#false,
#=:nonoverlayed=#false))
#=:consistent_overlay=#false))
end
# can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping)
macro _nothrow_meta()
Expand All @@ -314,7 +314,7 @@ macro _nothrow_meta()
#=:inaccessiblememonly=#false,
#=:noub=#false,
#=:noub_if_noinbounds=#false,
#=:nonoverlayed=#false))
#=:consistent_overlay=#false))
end
# can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping)
macro _noub_meta()
Expand All @@ -328,7 +328,7 @@ macro _noub_meta()
#=:inaccessiblememonly=#false,
#=:noub=#true,
#=:noub_if_noinbounds=#false,
#=:nonoverlayed=#false))
#=:consistent_overlay=#false))
end
# can be used in place of `@assume_effects :notaskstate` (supposed to be used for bootstrapping)
macro _notaskstate_meta()
Expand All @@ -342,7 +342,7 @@ macro _notaskstate_meta()
#=:inaccessiblememonly=#false,
#=:noub=#false,
#=:noub_if_noinbounds=#false,
#=:nonoverlayed=#false))
#=:consistent_overlay=#false))
end
# can be used in place of `@assume_effects :noub_if_noinbounds` (supposed to be used for bootstrapping)
macro _noub_if_noinbounds_meta()
Expand All @@ -356,7 +356,7 @@ macro _noub_if_noinbounds_meta()
#=:inaccessiblememonly=#false,
#=:noub=#false,
#=:noub_if_noinbounds=#true,
#=:nonoverlayed=#false))
#=:consistent_overlay=#false))
end

# another version of inlining that propagates an inbounds context
Expand Down

0 comments on commit 146a43a

Please sign in to comment.