From c675f21f492236ffc0b181d6382e1d79a197a3e2 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki Date: Wed, 1 May 2024 00:56:09 +0900 Subject: [PATCH] extend `:nonoverlayed` effect bit and add new `:consistent_overlay` override --- base/boot.jl | 2 +- base/compiler/abstractinterpretation.jl | 17 +++++--- base/compiler/compiler.jl | 6 +-- base/compiler/effects.jl | 57 ++++++++++++++++--------- base/compiler/inferencestate.jl | 5 ++- base/compiler/ssair/show.jl | 3 +- base/compiler/typeinfer.jl | 4 +- base/essentials.jl | 24 +++++------ base/expr.jl | 35 ++++++++++++--- src/julia.h | 24 ++++++----- src/method.c | 2 +- test/compiler/AbstractInterpreter.jl | 37 ++++++++-------- test/namedtuple.jl | 4 +- 13 files changed, 136 insertions(+), 84 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index 99a4856567d05..ab186f7260e5b 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -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 diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 510ba3a876f81..fea819cd7c707 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -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 @@ -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 @@ -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)) diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 8706b7ee1f750..a5eaef1344dab 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -47,7 +47,7 @@ struct EffectsOverride inaccessiblememonly::Bool noub::Bool noub_if_noinbounds::Bool - nonoverlayed::Bool + consistent_overlay::Bool end function EffectsOverride( override::EffectsOverride = @@ -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, @@ -72,7 +72,7 @@ function EffectsOverride( inaccessiblememonly, noub, noub_if_noinbounds, - nonoverlayed) + consistent_overlay) end const NUM_EFFECTS_OVERRIDES = 10 # sync with julia.h diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index 5bafd8a0bfad3..0375b8dba922c 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -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 @@ -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 @@ -105,7 +112,7 @@ struct Effects notaskstate::Bool inaccessiblememonly::UInt8 noub::UInt8 - nonoverlayed::Bool + nonoverlayed::UInt8 function Effects( consistent::UInt8, effect_free::UInt8, @@ -114,7 +121,7 @@ struct Effects notaskstate::Bool, inaccessiblememonly::UInt8, noub::UInt8, - nonoverlayed::Bool) + nonoverlayed::UInt8) return new( consistent, effect_free, @@ -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, @@ -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, @@ -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 @@ -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) = @@ -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) | @@ -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) @@ -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 diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 169d543f3249c..892b172cadaa2 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -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) diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index 7f2854959ce5e..82ca6e364f2fa 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -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 diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index a75edbed42cc7..cb3baf7d0f3f6 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -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 diff --git a/base/essentials.jl b/base/essentials.jl index 6293e99e1e0df..f3fcd459ef7f0 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -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() @@ -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() @@ -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() @@ -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() @@ -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() @@ -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() @@ -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() @@ -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() @@ -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() @@ -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() @@ -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() @@ -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 diff --git a/base/expr.jl b/base/expr.jl index cd66e5150b951..9173bf6f6e156 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -505,7 +505,7 @@ The following `setting`s are supported. - `:inaccessiblememonly` - `:noub` - `:noub_if_noinbounds` -- `:nonoverlayed` +- `:consistent_overlay` - `:foldable` - `:removable` - `:total` @@ -526,7 +526,7 @@ The `:consistent` setting asserts that for egal (`===`) inputs: !!! note The `:consistent`-cy assertion is made world-age wise. More formally, write - ``fᵢ`` for the evaluation of ``f`` in world-age ``i``, then we require: + ``fᵢ`` for the evaluation of ``f`` in world-age ``i``, then this setting requires: ```math ∀ i, x, y: x ≡ y → fᵢ(x) ≡ fᵢ(y) ``` @@ -675,8 +675,27 @@ any other effect assertions (such as `:consistent` or `:effect_free`) as well, b not model this, and they assume the absence of undefined behavior. --- -## `:nonoverlayed` -TODO. +## `:consistent_overlay` + +The `:consistent_overlay` setting asserts that any overlayed methods potentially called by +the method are `:consistent` with their original, non-overlayed counterparts. For the exact +definition of `:consistent`, refer to the earlier explanation. + +More formally, when evaluating a generic function call ``f(x)`` at a specific world-age ``i``, +and the regular method call ``fᵢ(x)`` is redirected to an overlay method ``fᵢ′(x)``, this +setting requires that ``fᵢ(x) ≡ fᵢ′(x)``. + +!!! note + Note that the requirements for `:consistent`-cy include not only that the return values + are egal, but also that the manner of termination is the same. + However, it's important to aware that when they throw exceptions, the exceptions + themselves don't necessarily have to be egal as explained in the note of `:consistent`. + In other words, if ``fᵢ(x)`` throws an exception, this settings requires ``fᵢ′(x)`` to + also raise one, but the exact exceptions may differ. + +!!! note + This setting isn't supported at the callsite; it has to be applied at the definition + site. Also, given its nature, it's expected to be used together with `Base.Experimental.@overlay`. --- ## `:foldable` @@ -754,6 +773,8 @@ macro assume_effects(args...) return Expr(:meta, form_purity_expr(override′)) else # call site annotation case + override.consistent_overlay && + throw(ArgumentError("Callsite `@assume_effects :consistent_overlay` is not supported")) return Expr(:block, form_purity_expr(override), Expr(:local, Expr(:(=), :val, esc(lastex))), @@ -767,7 +788,7 @@ function compute_assumed_settings(settings) for setting in settings override = compute_assumed_setting(override, setting) override === nothing && - throw(ArgumentError("@assume_effects $setting not supported")) + throw(ArgumentError("`@assume_effects $setting` not supported")) end return override end @@ -798,8 +819,8 @@ function compute_assumed_setting(override::EffectsOverride, @nospecialize(settin return EffectsOverride(override; noub = val) elseif setting === :noub_if_noinbounds return EffectsOverride(override; noub_if_noinbounds = val) - elseif setting === :nonoverlayed - return EffectsOverride(override; nonoverlayed = val) + elseif setting === :consistent_overlay + return EffectsOverride(override; consistent_overlay = val) elseif setting === :foldable consistent = effect_free = terminates_globally = noub = val return EffectsOverride(override; consistent, effect_free, terminates_globally, noub) diff --git a/src/julia.h b/src/julia.h index f2902a53f3fe5..68bd06883c0b0 100644 --- a/src/julia.h +++ b/src/julia.h @@ -279,7 +279,7 @@ typedef union __jl_purity_overrides_t { uint16_t ipo_inaccessiblememonly : 1; uint16_t ipo_noub : 1; uint16_t ipo_noub_if_noinbounds : 1; - uint16_t ipo_nonoverlayed : 1; + uint16_t ipo_consistent_overlay : 1; } overrides; uint16_t bits; } _jl_purity_overrides_t; @@ -458,22 +458,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_notaskstate : 2; + // uint8_t ipo_nothrow : 1; + // uint8_t ipo_terminates : 1; + // uint8_t ipo_notaskstate : 1; // uint8_t ipo_inaccessiblememonly : 2; + // uint8_t ipo_noub : 2; + // uint8_t ipo_nonoverlayed : 2; _Atomic(uint32_t) purity_bits; // purity_flags: - // uint8_t consistent : 2; + // uint8_t consistent : 3; // uint8_t effect_free : 2; - // uint8_t nothrow : 2; - // uint8_t terminates : 2; - // uint8_t nonoverlayed : 1; - // uint8_t notaskstate : 2; + // uint8_t nothrow : 1; + // uint8_t terminates : 1; + // uint8_t notaskstate : 1; // uint8_t inaccessiblememonly : 2; + // uint8_t noub : 2; + // uint8_t nonoverlayed : 2; jl_value_t *analysis_results; // Analysis results about this code (IPO-safe) // compilation state cache diff --git a/src/method.c b/src/method.c index e5c9b528335b1..050e4437df683 100644 --- a/src/method.c +++ b/src/method.c @@ -475,7 +475,7 @@ jl_code_info_t *jl_new_code_info_from_ir(jl_expr_t *ir) 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_noub_if_noinbounds = jl_unbox_bool(jl_exprarg(ma, 8)); - li->purity.overrides.ipo_nonoverlayed = jl_unbox_bool(jl_exprarg(ma, 9)); + li->purity.overrides.ipo_consistent_overlay = jl_unbox_bool(jl_exprarg(ma, 9)); } } else diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index 33be63961f7e6..eef01b3426335 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -135,25 +135,28 @@ Base.@assume_effects :total totalcall(f, args...) = f(args...) end |> only === Nothing # override `:native_executable` to allow concrete-eval for overlay-ed methods -Base.@assume_effects :foldable function gpucompiler384(x::Int) - 1 < x < 20 || error("x is too big") - return factorial(x) -end -@overlay OVERLAY_MT Base.@assume_effects :foldable :nonoverlayed function gpucompiler384(x::Int) - 1 < x < 20 || raise_on_gpu("x is too big") - return factorial(x) +function myfactorial(x::Int, raise) + res = 1 + 0 ≤ x < 20 || raise("x is too big") + Base.@assume_effects :terminates_locally while x > 1 + res *= x + x -= 1 + end + return res end -@noinline raise_on_gpu(x) = #=do something with GPU=# error(x) -call_gpucompiler384(x) = gpucompiler384(x) - -@test Base.infer_effects(gpucompiler384, (Int,); interp=MTOverlayInterp()) |> Core.Compiler.is_nonoverlayed -@test Base.infer_effects(call_gpucompiler384, (Int,); interp=MTOverlayInterp()) |> Core.Compiler.is_nonoverlayed - -@test Base.infer_return_type(; interp=MTOverlayInterp()) do - Val(gpucompiler384(3)) -end == Val{6} +raise_on_gpu1(x) = error(x) +@overlay OVERLAY_MT @noinline raise_on_gpu1(x) = #=do something with GPU=# error(x) +raise_on_gpu2(x) = error(x) +@overlay OVERLAY_MT @noinline Base.@assume_effects :consistent_overlay raise_on_gpu2(x) = #=do something with GPU=# error(x) +cpu_factorial(x::Int) = myfactorial(x, error) +gpu_factorial1(x::Int) = myfactorial(x, raise_on_gpu1) +gpu_factorial2(x::Int) = myfactorial(x, raise_on_gpu2) + +@test Base.infer_effects(cpu_factorial, (Int,); interp=MTOverlayInterp()) |> Core.Compiler.is_nonoverlayed +@test Base.infer_effects(gpu_factorial1, (Int,); interp=MTOverlayInterp()) |> !Core.Compiler.is_nonoverlayed +@test Base.infer_effects(gpu_factorial2, (Int,); interp=MTOverlayInterp()) |> Core.Compiler.is_consistent_overlay @test Base.infer_return_type(; interp=MTOverlayInterp()) do - Val(call_gpucompiler384(3)) + Val(gpu_factorial2(3)) end == Val{6} # GPUCompiler needs accurate inference through kwfunc with the overlay of `Core.throw_inexacterror` diff --git a/test/namedtuple.jl b/test/namedtuple.jl index 48aa8ea4a2591..558ba94bda8d0 100644 --- a/test/namedtuple.jl +++ b/test/namedtuple.jl @@ -398,14 +398,14 @@ for f in (Base.merge, Base.structdiff) fallback_func(a::NamedTuple, b::NamedTuple) = @invoke f(a::NamedTuple, b::NamedTuple) @testset let eff = Base.infer_effects(fallback_func) @test Core.Compiler.is_foldable(eff) - @test eff.nonoverlayed + @test Core.Compiler.is_nonoverlayed(eff) end @test only(Base.return_types(fallback_func)) == NamedTuple # test if `max_methods = 4` setting works as expected general_func(a::NamedTuple, b::NamedTuple) = f(a, b) @testset let eff = Base.infer_effects(general_func) @test Core.Compiler.is_foldable(eff) - @test eff.nonoverlayed + @test Core.Compiler.is_nonoverlayed(eff) end @test only(Base.return_types(general_func)) == NamedTuple end