Skip to content

Commit

Permalink
effects: allow override of :nonoverlayed effect bit
Browse files Browse the repository at this point in the history
Certain external `AbstractInterpreters`, such as GPUCompiler.jl, have
long sought the ability to allow concrete evaluation for specific
overlay-ed methods to achieve optimal inference accuracy. This is
currently not permitted, although it should be safe when an overlay-ed
method has the same semantics as the original method, and its result can
be safely replaced with the result of the original method. Refer to
JuliaGPU/GPUCompiler.jl#384 for more examples.

To address this issue, this commit introduces the capability to override
the `:nonoverlayed` effect bit using `@assume_effects`. With the
enhancements in PR #51078, this override behaves similarly to other
effect bits. Consequently, external `AbstractInterpreters` can utilize
this feature to permit concrete evaluation for annotated overlay-ed
methods, e.g.
```julia
@overlay OVERLAY_MT Base.@assume_effects :nonoverlayed f(x) = [...]
```

However, it now seems awkward to annotate a method with `Base.@assume_effects :nonoverlayed`
when it is actually marked with `@overlay`. A more intuitive terminology,
like `native_executable`, might be more appropriate for renaming the
`:nonoverlayed` effect bit.
  • Loading branch information
aviatesk committed Aug 29, 2023
1 parent 00b5060 commit 0778175
Show file tree
Hide file tree
Showing 13 changed files with 116 additions and 66 deletions.
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,
#=:nonoverlayed=#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
2 changes: 1 addition & 1 deletion base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
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
39 changes: 21 additions & 18 deletions base/compiler/effects.jl
Original file line number Diff line number Diff line change
Expand Up @@ -258,29 +258,32 @@ struct EffectsOverride
notaskstate::Bool
inaccessiblememonly::Bool
noub::Bool
nonoverlayed::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.nonoverlayed && (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
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, :nonoverlayed)
ipo_effects = Effects(ipo_effects; nonoverlayed=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,
#=:nonoverlayed=#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,
#=:nonoverlayed=#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,
#=:nonoverlayed=#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,
#=:nonoverlayed=#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,
#=:nonoverlayed=#false))
end

# another version of inlining that propagates an inbounds context
Expand Down
16 changes: 11 additions & 5 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -714,8 +714,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, nonoverlayed) =
(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 +735,8 @@ macro assume_effects(args...)
inaccessiblememonly = val
elseif setting === :noub
noub = val
elseif setting === :nonoverlayed
nonoverlayed = val
elseif setting === :foldable
consistent = effect_free = terminates_globally = noub = val
elseif setting === :removable
Expand All @@ -746,15 +749,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, nonoverlayed))
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, nonoverlayed)))
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, nonoverlayed))
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
27 changes: 15 additions & 12 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,9 @@ typedef union __jl_purity_overrides_t {
uint8_t ipo_notaskstate : 1;
uint8_t ipo_inaccessiblememonly : 1;
uint8_t ipo_noub : 1;
uint8_t ipo_nonoverlayed : 1;
} overrides;
uint8_t bits;
uint32_t bits;
} _jl_purity_overrides_t;

// This type describes a single function body
Expand Down Expand Up @@ -428,22 +429,24 @@ typedef struct _jl_code_instance_t {
// see also encode_effects() and decode_effects() in `base/compiler/effects.jl`,
uint32_t ipo_purity_bits;
// ipo_purity_flags:
// uint8_t ipo_consistent : 2;
// uint8_t ipo_consistent : 3;
// uint8_t ipo_effect_free : 2;
// uint8_t ipo_nothrow : 2;
// uint8_t ipo_terminates : 2;
// uint8_t ipo_nonoverlayed : 1;
// uint8_t ipo_nothrow : 1;
// uint8_t ipo_terminates : 1;
// uint8_t ipo_notaskstate : 2;
// uint8_t ipo_inaccessiblememonly : 2;
// uint8_t ipo_nonoverlayed : 1;
// uint8_t ipo_noinbounds : 1;
_Atomic(uint32_t) purity_bits;
// purity_flags:
// uint8_t consistent : 2;
// uint8_t effect_free : 2;
// uint8_t nothrow : 2;
// uint8_t terminates : 2;
// uint8_t nonoverlayed : 1;
// uint8_t notaskstate : 2;
// uint8_t inaccessiblememonly : 2;
// uint8_t ipo_consistent : 3;
// uint8_t ipo_effect_free : 2;
// uint8_t ipo_nothrow : 1;
// uint8_t ipo_terminates : 1;
// uint8_t ipo_notaskstate : 2;
// uint8_t ipo_inaccessiblememonly : 2;
// uint8_t ipo_nonoverlayed : 1;
// uint8_t ipo_noinbounds : 1;
jl_value_t *argescapes; // escape information of call arguments

// compilation state cache
Expand Down
5 changes: 3 additions & 2 deletions src/method.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ static jl_value_t *resolve_globals(jl_value_t *expr, jl_module_t *module, jl_sve
jl_error("In ccall calling convention, expected two argument tuple or symbol.");
}
JL_TYPECHK(ccall method definition, symbol, jl_get_nth_field(cc, 0));
JL_TYPECHK(ccall method definition, uint8, jl_get_nth_field(cc, 1));
JL_TYPECHK(ccall method definition, uint32, jl_get_nth_field(cc, 1));
}
jl_exprargset(e, 0, resolve_globals(jl_exprarg(e, 0), module, sparam_vals, binding_effects, 1));
i++;
Expand Down Expand Up @@ -328,7 +328,7 @@ static void jl_code_info_set_ir(jl_code_info_t *li, jl_expr_t *ir)
else if (ma == (jl_value_t*)jl_no_constprop_sym)
li->constprop = 2;
else if (jl_is_expr(ma) && ((jl_expr_t*)ma)->head == jl_purity_sym) {
if (jl_expr_nargs(ma) == 8) {
if (jl_expr_nargs(ma) == 9) {
li->purity.overrides.ipo_consistent = jl_unbox_bool(jl_exprarg(ma, 0));
li->purity.overrides.ipo_effect_free = jl_unbox_bool(jl_exprarg(ma, 1));
li->purity.overrides.ipo_nothrow = jl_unbox_bool(jl_exprarg(ma, 2));
Expand All @@ -337,6 +337,7 @@ static void jl_code_info_set_ir(jl_code_info_t *li, jl_expr_t *ir)
li->purity.overrides.ipo_notaskstate = jl_unbox_bool(jl_exprarg(ma, 5));
li->purity.overrides.ipo_inaccessiblememonly = jl_unbox_bool(jl_exprarg(ma, 6));
li->purity.overrides.ipo_noub = jl_unbox_bool(jl_exprarg(ma, 7));
li->purity.overrides.ipo_nonoverlayed = jl_unbox_bool(jl_exprarg(ma, 8));
}
}
else
Expand Down
9 changes: 5 additions & 4 deletions stdlib/Serialization/src/Serialization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const TAGS = Any[
const NTAGS = length(TAGS)
@assert NTAGS == 255

const ser_version = 24 # do not make changes without bumping the version #!
const ser_version = 25 # do not make changes without bumping the version #!

format_version(::AbstractSerializer) = ser_version
format_version(s::Serializer) = s.version
Expand Down Expand Up @@ -1028,7 +1028,8 @@ function deserialize(s::AbstractSerializer, ::Type{Method})
isva = deserialize(s)::Bool
is_for_opaque_closure = false
nospecializeinfer = false
constprop = purity = 0x00
constprop = 0x00
purity = format_version(s) >= 25 ? zero(UInt32) : 0x00
template_or_is_opaque = deserialize(s)
if isa(template_or_is_opaque, Bool)
is_for_opaque_closure = template_or_is_opaque
Expand All @@ -1039,7 +1040,7 @@ function deserialize(s::AbstractSerializer, ::Type{Method})
constprop = deserialize(s)::UInt8
end
if format_version(s) >= 17
purity = deserialize(s)::UInt8
purity = deserialize(s)::(format_version(s) >= 25 ? UInt32 : UInt8)
end
template = deserialize(s)
else
Expand Down Expand Up @@ -1211,7 +1212,7 @@ function deserialize(s::AbstractSerializer, ::Type{CodeInfo})
ci.constprop = deserialize(s)::UInt8
end
if format_version(s) >= 17
ci.purity = deserialize(s)::UInt8
ci.purity = deserialize(s)::(format_version(s) >= 25 ? UInt32 : UInt8)
end
if format_version(s) >= 22
ci.inlining_cost = deserialize(s)::UInt16
Expand Down
Loading

0 comments on commit 0778175

Please sign in to comment.