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: allow override of :nonoverlayed effect bit (and rename it to :native_executable) #51080

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,8 @@ macro _foldable_meta()
#=:terminates_locally=#false,
#=:notaskstate=#false,
#=:inaccessiblememonly=#false,
#=:noub=#true))
#=:noub=#true,
#=:native_executable=#false))
end

const NTuple{N,T} = Tuple{Vararg{T,N}}
Expand Down
2 changes: 1 addition & 1 deletion base/c.jl
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,6 @@ macro ccall(expr)
return ccall_macro_lower(:ccall, ccall_macro_parse(expr)...)
end

macro ccall_effects(effects::UInt8, expr)
macro ccall_effects(effects::UInt32, expr)
return ccall_macro_lower((:ccall, effects), ccall_macro_parse(expr)...)
end
8 changes: 4 additions & 4 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -451,10 +451,10 @@ function add_call_backedges!(interp::AbstractInterpreter, @nospecialize(rettype)
# don't bother to add backedges when both type and effects information are already
# maximized to the top since a new method couldn't refine or widen them anyway
if rettype === Any
# ignore the `:nonoverlayed` property if `interp` doesn't use overlayed method table
# ignore the `:native_executable` 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; native_executable=false)
end
if (# ignore the `:noinbounds` property if `:consistent`-cy is tainted already
(sv isa InferenceState && sv.ipo_effects.consistent === ALWAYS_FALSE) ||
Expand Down Expand Up @@ -854,7 +854,7 @@ 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_native_executable(interp) || is_native_executable(effects)
return :concrete_eval
end
# disable concrete-evaluation if this function call is tainted by some overlayed
Expand Down Expand Up @@ -2557,7 +2557,7 @@ function abstract_eval_foreigncall(interp::AbstractInterpreter, e::Expr, vtypes:
abstract_eval_value(interp, x, vtypes, sv)
end
cconv = e.args[5]
if isa(cconv, QuoteNode) && (v = cconv.value; isa(v, Tuple{Symbol, UInt8}))
if isa(cconv, QuoteNode) && (v = cconv.value; isa(v, Tuple{Symbol, UInt32}))
override = decode_effects_override(v[2])
effects = Effects(effects;
consistent = override.consistent ? ALWAYS_TRUE : effects.consistent,
Expand Down
63 changes: 34 additions & 29 deletions base/compiler/effects.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ following meanings:
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::Bool`: indicates that any methods that may be called within this method
are not defined in an [overlayed method table](@ref OverlayMethodTable).
- `native_executable::Bool`: indicates whether this method can be executed using Julia's
native compiler and runtime. Note that in particular this is generally not the case when
any methods defined in an [`OverlayMethodTable`](OverlayMethodTable) can be called.
- `noinbounds::Bool`: If set, indicates that this method does not read the parent's `:inbounds`
state. In particular, it does not have any reached `:boundscheck` exprs, not propagates inbounds
to any children that do.
Expand Down Expand Up @@ -91,7 +92,8 @@ The output represents the state of different effect properties in the following
- `+i` (green): `true`
- `-i` (red): `false`

Additionally, if the `nonoverlayed` property is false, a red prime symbol (′) is displayed after the tuple.
Additionally, if the `native_executable` property is `false`,
a red prime symbol (′) is displayed after the tuple.
"""
struct Effects
consistent::UInt8
Expand All @@ -101,7 +103,7 @@ struct Effects
notaskstate::Bool
inaccessiblememonly::UInt8
noub::Bool
nonoverlayed::Bool
native_executable::Bool
noinbounds::Bool
function Effects(
consistent::UInt8,
Expand All @@ -111,7 +113,7 @@ struct Effects
notaskstate::Bool,
inaccessiblememonly::UInt8,
noub::Bool,
nonoverlayed::Bool,
native_executable::Bool,
noinbounds::Bool)
return new(
consistent,
Expand All @@ -121,7 +123,7 @@ struct Effects
notaskstate,
inaccessiblememonly,
noub,
nonoverlayed,
native_executable,
noinbounds)
end
end
Expand Down Expand Up @@ -152,7 +154,7 @@ function Effects(effects::Effects = _EFFECTS_UNKNOWN;
notaskstate::Bool = effects.notaskstate,
inaccessiblememonly::UInt8 = effects.inaccessiblememonly,
noub::Bool = effects.noub,
nonoverlayed::Bool = effects.nonoverlayed,
native_executable::Bool = effects.native_executable,
noinbounds::Bool = effects.noinbounds)
return Effects(
consistent,
Expand All @@ -162,7 +164,7 @@ function Effects(effects::Effects = _EFFECTS_UNKNOWN;
notaskstate,
inaccessiblememonly,
noub,
nonoverlayed,
native_executable,
noinbounds)
end

Expand All @@ -175,7 +177,7 @@ function merge_effects(old::Effects, new::Effects)
merge_effectbits(old.notaskstate, new.notaskstate),
merge_effectbits(old.inaccessiblememonly, new.inaccessiblememonly),
merge_effectbits(old.noub, new.noub),
merge_effectbits(old.nonoverlayed, new.nonoverlayed),
merge_effectbits(old.native_executable, new.native_executable),
merge_effectbits(old.noinbounds, new.noinbounds))
end

Expand All @@ -194,7 +196,7 @@ is_terminates(effects::Effects) = effects.terminates
is_notaskstate(effects::Effects) = effects.notaskstate
is_inaccessiblememonly(effects::Effects) = effects.inaccessiblememonly === ALWAYS_TRUE
is_noub(effects::Effects) = effects.noub
is_nonoverlayed(effects::Effects) = effects.nonoverlayed
is_native_executable(effects::Effects) = effects.native_executable

# implies `is_notaskstate` & `is_inaccessiblememonly`, but not explicitly checked here
is_foldable(effects::Effects) =
Expand Down Expand Up @@ -232,7 +234,7 @@ function encode_effects(e::Effects)
((e.notaskstate % UInt32) << 7) |
((e.inaccessiblememonly % UInt32) << 8) |
((e.noub % UInt32) << 10) |
((e.nonoverlayed % UInt32) << 11) |
((e.native_executable % UInt32) << 11) |
((e.noinbounds % UInt32) << 12)
end

Expand All @@ -258,29 +260,32 @@ struct EffectsOverride
notaskstate::Bool
inaccessiblememonly::Bool
noub::Bool
native_executable::Bool
end

function encode_effects_override(eo::EffectsOverride)
e = 0x00
eo.consistent && (e |= (0x01 << 0))
eo.effect_free && (e |= (0x01 << 1))
eo.nothrow && (e |= (0x01 << 2))
eo.terminates_globally && (e |= (0x01 << 3))
eo.terminates_locally && (e |= (0x01 << 4))
eo.notaskstate && (e |= (0x01 << 5))
eo.inaccessiblememonly && (e |= (0x01 << 6))
eo.noub && (e |= (0x01 << 7))
e = zero(UInt32)
eo.consistent && (e |= (1 << 0) % UInt32)
eo.effect_free && (e |= (1 << 1) % UInt32)
eo.nothrow && (e |= (1 << 2) % UInt32)
eo.terminates_globally && (e |= (1 << 3) % UInt32)
eo.terminates_locally && (e |= (1 << 4) % UInt32)
eo.notaskstate && (e |= (1 << 5) % UInt32)
eo.inaccessiblememonly && (e |= (1 << 6) % UInt32)
eo.noub && (e |= (1 << 7) % UInt32)
eo.native_executable && (e |= (1 << 8) % UInt32)
return e
end

function decode_effects_override(e::UInt8)
function decode_effects_override(e::UInt32)
return EffectsOverride(
(e & (0x01 << 0)) != 0x00,
(e & (0x01 << 1)) != 0x00,
(e & (0x01 << 2)) != 0x00,
(e & (0x01 << 3)) != 0x00,
(e & (0x01 << 4)) != 0x00,
(e & (0x01 << 5)) != 0x00,
(e & (0x01 << 6)) != 0x00,
(e & (0x01 << 7)) != 0x00)
!iszero(e & (1 << 0)),
!iszero(e & (1 << 1)),
!iszero(e & (1 << 2)),
!iszero(e & (1 << 3)),
!iszero(e & (1 << 4)),
!iszero(e & (1 << 5)),
!iszero(e & (1 << 6)),
!iszero(e & (1 << 7)),
!iszero(e & (1 << 8)))
end
7 changes: 4 additions & 3 deletions base/compiler/inferencestate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ mutable struct InferenceState
end

if def isa Method
ipo_effects = Effects(ipo_effects; nonoverlayed=is_nonoverlayed(def))
ipo_effects = Effects(ipo_effects; native_executable=is_native_executable(def))
end

restrict_abstract_call_sites = isa(def, Module)
Expand All @@ -318,8 +318,9 @@ mutable struct InferenceState
end
end

is_nonoverlayed(m::Method) = !isdefined(m, :external_mt)
is_nonoverlayed(interp::AbstractInterpreter) = !isoverlayed(method_table(interp))
is_native_executable(m::Method) = !isdefined(m, :external_mt)
is_native_executable(interp::AbstractInterpreter) = !isoverlayed(method_table(interp))

isoverlayed(::MethodTableView) = error("unsatisfied MethodTableView interface")
isoverlayed(::InternalMethodTable) = false
isoverlayed(::OverlayMethodTable) = true
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/ssair/irinterp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function concrete_eval_invoke(interp::AbstractInterpreter,
argtypes === nothing && return Pair{Any,Bool}(Bottom, false)
effects = decode_effects(code.ipo_purity_bits)
if (is_foldable(effects) && is_all_const_arg(argtypes, #=start=#1) &&
(is_nonoverlayed(interp) || is_nonoverlayed(effects)))
(is_native_executable(interp) || is_native_executable(effects)))
args = collect_const_args(argtypes, #=start=#1)
value = let world = get_world_counter(interp)
try
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/ssair/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1020,7 +1020,7 @@ function Base.show(io::IO, e::Effects)
print(io, ',')
printstyled(io, effectbits_letter(e, :noinbounds, 'i'); color=effectbits_color(e, :noinbounds))
print(io, ')')
e.nonoverlayed || printstyled(io, '′'; color=:red)
e.native_executable || printstyled(io, '′'; color=:red)
end

@specialize
3 changes: 3 additions & 0 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,9 @@ function adjust_effects(sv::InferenceState)
if is_effect_overridden(override, :noub)
ipo_effects = Effects(ipo_effects; noub=true)
end
if is_effect_overridden(override, :native_executable)
ipo_effects = Effects(ipo_effects; native_executable=true)
end
end

return ipo_effects
Expand Down
15 changes: 10 additions & 5 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,8 @@ macro _total_meta()
#=:terminates_locally=#false,
#=:notaskstate=#true,
#=:inaccessiblememonly=#true,
#=:noub=#true))
#=:noub=#true,
#=:native_executable=#false))
end
# can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping)
macro _foldable_meta()
Expand All @@ -221,7 +222,8 @@ macro _foldable_meta()
#=:terminates_locally=#false,
#=:notaskstate=#false,
#=:inaccessiblememonly=#true,
#=:noub=#true))
#=:noub=#true,
#=:native_executable=#false))
end
# can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping)
macro _nothrow_meta()
Expand All @@ -233,7 +235,8 @@ macro _nothrow_meta()
#=:terminates_locally=#false,
#=:notaskstate=#false,
#=:inaccessiblememonly=#false,
#=:noub=#false))
#=:noub=#false,
#=:native_executable=#false))
end
# can be used in place of `@assume_effects :terminates_locally` (supposed to be used for bootstrapping)
macro _terminates_locally_meta()
Expand All @@ -245,7 +248,8 @@ macro _terminates_locally_meta()
#=:terminates_locally=#true,
#=:notaskstate=#false,
#=:inaccessiblememonly=#false,
#=:noub=#false))
#=:noub=#false,
#=:native_executable=#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 @@ -257,7 +261,8 @@ macro _effect_free_terminates_locally_meta()
#=:terminates_locally=#true,
#=:notaskstate=#false,
#=:inaccessiblememonly=#false,
#=:noub=#false))
#=:noub=#false,
#=:native_executable=#false))
end

# another version of inlining that propagates an inbounds context
Expand Down
28 changes: 23 additions & 5 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ The following `setting`s are supported.
- `:notaskstate`
- `:inaccessiblememonly`
- `:noub`
- `:native_executable`
- `:foldable`
- `:removable`
- `:total`
Expand Down Expand Up @@ -641,6 +642,17 @@ The `:noub` setting asserts that the method will not execute any undefined behav
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.

---
## `:native_executable`

The `:native_executable` setting asserts that this method can be executed using Julia's
native compiler and runtime. Currently this particularly implies that any methods defined
in an [`OverlayMethodTable`](@ref Core.Compiler.OverlayMethodTable) (including this method
in question) are never be called during executing the method. However, it is worth noting
that it is safe to annotate [`@overlay`](@ref Base.Experimental.@overlay) method as
`:native_executable` when the overlay-ed method has the same semantics as the original
method and its result can safely be replaced with the result of the original method.

---
## `:foldable`

Expand Down Expand Up @@ -714,8 +726,9 @@ macro assume_effects(args...)
ex = nothing
idx = length(args)
end
(consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly, noub) =
(false, false, false, false, false, false, false, false, false)
(consistent, effect_free, nothrow, terminates_globally, terminates_locally,
notaskstate, inaccessiblememonly, noub, native_executable) =
(false, false, false, false, false, false, false, false, false, false)
for org_setting in args[1:idx]
(setting, val) = compute_assumed_setting(org_setting)
if setting === :consistent
Expand All @@ -734,6 +747,8 @@ macro assume_effects(args...)
inaccessiblememonly = val
elseif setting === :noub
noub = val
elseif setting === :native_executable
native_executable = val
elseif setting === :foldable
consistent = effect_free = terminates_globally = noub = val
elseif setting === :removable
Expand All @@ -746,15 +761,18 @@ macro assume_effects(args...)
end
if is_function_def(inner)
return esc(pushmeta!(ex, :purity,
consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly, noub))
consistent, effect_free, nothrow, terminates_globally, terminates_locally,
notaskstate, inaccessiblememonly, noub, native_executable))
elseif isexpr(ex, :macrocall) && ex.args[1] === Symbol("@ccall")
ex.args[1] = GlobalRef(Base, Symbol("@ccall_effects"))
insert!(ex.args, 3, Core.Compiler.encode_effects_override(Core.Compiler.EffectsOverride(
consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly, noub)))
consistent, effect_free, nothrow, terminates_globally, terminates_locally,
notaskstate, inaccessiblememonly, noub, native_executable)))
return esc(ex)
else # anonymous function case
return Expr(:meta, Expr(:purity,
consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly, noub))
consistent, effect_free, nothrow, terminates_globally, terminates_locally,
notaskstate, inaccessiblememonly, noub, native_executable))
end
end

Expand Down
6 changes: 5 additions & 1 deletion base/strings/string.jl
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ end

# This is @assume_effects :effect_free :nothrow :terminates_globally @ccall jl_alloc_string(n::Csize_t)::Ref{String},
# but the macro is not available at this time in bootstrap, so we write it manually.
@eval _string_n(n::Integer) = $(Expr(:foreigncall, QuoteNode(:jl_alloc_string), Ref{String}, Expr(:call, Expr(:core, :svec), :Csize_t), 1, QuoteNode((:ccall,0xe)), :(convert(Csize_t, n))))
@eval function _string_n(n::Integer)
return $(Expr(:foreigncall, QuoteNode(:jl_alloc_string), Ref{String},
Expr(:call, Expr(:core, :svec), :Csize_t), 1, QuoteNode((:ccall, 0x0000000e)),
:(convert(Csize_t, n))))
end

"""
String(s::AbstractString)
Expand Down
4 changes: 2 additions & 2 deletions src/jltypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -3005,7 +3005,7 @@ void jl_init_types(void) JL_GC_DISABLED
jl_bool_type,
jl_uint8_type,
jl_uint8_type,
jl_uint8_type,
jl_uint32_type,
jl_uint16_type),
jl_emptysvec,
0, 1, 22);
Expand Down Expand Up @@ -3074,7 +3074,7 @@ void jl_init_types(void) JL_GC_DISABLED
jl_bool_type,
jl_uint8_type,
jl_uint8_type,
jl_uint8_type),
jl_uint32_type),
jl_emptysvec,
0, 1, 10);
//const static uint32_t method_constfields[1] = { 0x03fc065f }; // (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<6)|(1<<9)|(1<<10)|(1<<18)|(1<<19)|(1<<20)|(1<<21)|(1<<22)|(1<<23)|(1<<24)|(1<<25);
Expand Down