Skip to content

Commit

Permalink
Add notaskstate effect (#45422)
Browse files Browse the repository at this point in the history
Split out from #45272. This effect models the legality of moving code
between tasks. It is somewhat related to effect-free/consistent, but
only with respect to task-local state. As an example consider something
like:

```
global glob
function bar()
    @async (global glob = 1; some_other_code())
end
```

The newly created task is not effect-free, but it would be legal to inline
the assignment of `glob` into `bar` (as long it is inlined before the creation
of the task of `some_other_code` does not access `glob`). For comparison,
the following is neither `notls`, nor `effect_free`:

```
function bar()
    @async (task_local_storage()[:var] = 1; some_other_code())
end
```

The same implies to implicit task-local state such as the RNG state.

Implementation wise, there isn't a lot here, because the implicit
tainting by ccall is the correct conservative default. In the future,
we may want to annotate various ccalls as being permissible for
notls, but let's worry about that when we have a case that needs it.
  • Loading branch information
Keno authored May 24, 2022
1 parent 8512dd2 commit 8bb973a
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 7 deletions.
3 changes: 2 additions & 1 deletion base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1998,7 +1998,8 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e),
effects.effect_free ? ALWAYS_TRUE : TRISTATE_UNKNOWN,
effects.nothrow ? ALWAYS_TRUE : TRISTATE_UNKNOWN,
effects.terminates_globally ? ALWAYS_TRUE : TRISTATE_UNKNOWN,
#=nonoverlayed=#true
#=nonoverlayed=#true,
#=notaskstate=#TRISTATE_UNKNOWN
))
else
tristate_merge!(sv, EFFECTS_UNKNOWN)
Expand Down
2 changes: 2 additions & 0 deletions base/compiler/ssair/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,8 @@ function Base.show(io::IO, e::Core.Compiler.Effects)
printstyled(io, string(tristate_letter(e.nothrow), 'n'); color=tristate_color(e.nothrow))
print(io, ',')
printstyled(io, string(tristate_letter(e.terminates), 't'); color=tristate_color(e.terminates))
print(io, ',')
printstyled(io, string(tristate_letter(e.notaskstate), 's'); color=tristate_color(e.notaskstate))
print(io, ')')
e.nonoverlayed || printstyled(io, ''; color=:red)
end
Expand Down
27 changes: 21 additions & 6 deletions base/compiler/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ The effects are composed of the following set of different properties:
- `terminates::TriState`: this method is guaranteed to terminate
- `nonoverlayed::Bool`: indicates that any methods that may be called within this method
are not defined in an [overlayed method table](@ref OverlayMethodTable)
- `notaskstate::TriState`: this method does not access any state bound to the current
task and may thus be moved to a different task without changing observable
behavior. Note that this currently implies that `noyield` as well, since
yielding modifies the state of the current task, though this may be split
in the future.
See [`Base.@assume_effects`](@ref) for more detailed explanation on the definitions of these properties.
Along the abstract interpretation, `Effects` at each statement are analyzed locally and
Expand All @@ -67,6 +72,7 @@ struct Effects
nothrow::TriState
terminates::TriState
nonoverlayed::Bool
notaskstate::TriState
# This effect is currently only tracked in inference and modified
# :consistent before caching. We may want to track it in the future.
inbounds_taints_consistency::Bool
Expand All @@ -76,41 +82,46 @@ function Effects(
effect_free::TriState,
nothrow::TriState,
terminates::TriState,
nonoverlayed::Bool)
nonoverlayed::Bool,
notaskstate::TriState)
return Effects(
consistent,
effect_free,
nothrow,
terminates,
nonoverlayed,
notaskstate,
false)
end

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

function Effects(e::Effects = EFFECTS_UNKNOWN′;
consistent::TriState = e.consistent,
effect_free::TriState = e.effect_free,
nothrow::TriState = e.nothrow,
terminates::TriState = e.terminates,
nonoverlayed::Bool = e.nonoverlayed,
notaskstate::TriState = e.notaskstate,
inbounds_taints_consistency::Bool = e.inbounds_taints_consistency)
return Effects(
consistent,
effect_free,
nothrow,
terminates,
nonoverlayed,
notaskstate,
inbounds_taints_consistency)
end

is_consistent(effects::Effects) = effects.consistent === ALWAYS_TRUE
is_effect_free(effects::Effects) = effects.effect_free === ALWAYS_TRUE
is_nothrow(effects::Effects) = effects.nothrow === ALWAYS_TRUE
is_terminates(effects::Effects) = effects.terminates === ALWAYS_TRUE
is_notaskstate(effects::Effects) = effects.notaskstate === ALWAYS_TRUE
is_nonoverlayed(effects::Effects) = effects.nonoverlayed

is_concrete_eval_eligible(effects::Effects) =
Expand All @@ -132,7 +143,8 @@ function encode_effects(e::Effects)
(e.effect_free.state << 2) |
(e.nothrow.state << 4) |
(e.terminates.state << 6) |
(UInt32(e.nonoverlayed) << 8)
(UInt32(e.nonoverlayed) << 8) |
(UInt32(e.notaskstate.state) << 9)
end
function decode_effects(e::UInt32)
return Effects(
Expand All @@ -141,6 +153,7 @@ function decode_effects(e::UInt32)
TriState((e >> 4) & 0x03),
TriState((e >> 6) & 0x03),
_Bool( (e >> 8) & 0x01),
TriState((e >> 9) & 0x03),
false)
end

Expand All @@ -155,6 +168,8 @@ function tristate_merge(old::Effects, new::Effects)
tristate_merge(
old.terminates, new.terminates),
old.nonoverlayed & new.nonoverlayed,
tristate_merge(
old.notaskstate, new.notaskstate),
old.inbounds_taints_consistency | new.inbounds_taints_consistency)
end

Expand Down

0 comments on commit 8bb973a

Please sign in to comment.