Skip to content

Commit a9f342e

Browse files
committed
Add notaskstate effect
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.
1 parent a37dd16 commit a9f342e

File tree

3 files changed

+25
-7
lines changed

3 files changed

+25
-7
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1998,7 +1998,8 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e),
19981998
effects.effect_free ? ALWAYS_TRUE : TRISTATE_UNKNOWN,
19991999
effects.nothrow ? ALWAYS_TRUE : TRISTATE_UNKNOWN,
20002000
effects.terminates_globally ? ALWAYS_TRUE : TRISTATE_UNKNOWN,
2001-
#=nonoverlayed=#true
2001+
#=nonoverlayed=#true,
2002+
#=notaskstate=#TRISTATE_UNKNOWN
20022003
))
20032004
else
20042005
tristate_merge!(sv, EFFECTS_UNKNOWN)

base/compiler/ssair/show.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,8 @@ function Base.show(io::IO, e::Core.Compiler.Effects)
802802
printstyled(io, string(tristate_letter(e.nothrow), 'n'); color=tristate_color(e.nothrow))
803803
print(io, ',')
804804
printstyled(io, string(tristate_letter(e.terminates), 't'); color=tristate_color(e.terminates))
805+
print(io, ',')
806+
printstyled(io, string(tristate_letter(e.notaskstate), 's'); color=tristate_color(e.notaskstate))
805807
print(io, ')')
806808
e.nonoverlayed || printstyled(io, ''; color=:red)
807809
end

base/compiler/types.jl

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ The effects are composed of the following set of different properties:
4545
- `terminates::TriState`: this method is guaranteed to terminate
4646
- `nonoverlayed::Bool`: indicates that any methods that may be called within this method
4747
are not defined in an [overlayed method table](@ref OverlayMethodTable)
48+
- `notaskstate::TriState`: this method does not access any state bound to the current
49+
task and may thus be moved to a different task without changing observable
50+
behavior. Note that this currently implies that `noyield` as well, since
51+
yielding modifies the state of the current task, though this may be split
52+
in the future.
4853
See [`Base.@assume_effects`](@ref) for more detailed explanation on the definitions of these properties.
4954
5055
Along the abstract interpretation, `Effects` at each statement are analyzed locally and
@@ -67,6 +72,7 @@ struct Effects
6772
nothrow::TriState
6873
terminates::TriState
6974
nonoverlayed::Bool
75+
notaskstate::TriState
7076
# This effect is currently only tracked in inference and modified
7177
# :consistent before caching. We may want to track it in the future.
7278
inbounds_taints_consistency::Bool
@@ -76,41 +82,46 @@ function Effects(
7682
effect_free::TriState,
7783
nothrow::TriState,
7884
terminates::TriState,
79-
nonoverlayed::Bool)
85+
nonoverlayed::Bool,
86+
notaskstate::TriState)
8087
return Effects(
8188
consistent,
8289
effect_free,
8390
nothrow,
8491
terminates,
8592
nonoverlayed,
93+
notaskstate,
8694
false)
8795
end
8896

89-
const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true)
90-
const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, TRISTATE_UNKNOWN, ALWAYS_TRUE, true)
91-
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)
92-
const EFFECTS_UNKNOWN′ = Effects(TRISTATE_UNKNOWN, TRISTATE_UNKNOWN, TRISTATE_UNKNOWN, TRISTATE_UNKNOWN, false) # unknown, really
97+
const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true, ALWAYS_TRUE)
98+
const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, TRISTATE_UNKNOWN, ALWAYS_TRUE, true, ALWAYS_TRUE)
99+
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)
100+
const EFFECTS_UNKNOWN′ = Effects(TRISTATE_UNKNOWN, TRISTATE_UNKNOWN, TRISTATE_UNKNOWN, TRISTATE_UNKNOWN, false, TRISTATE_UNKNOWN) # unknown, really
93101

94102
function Effects(e::Effects = EFFECTS_UNKNOWN′;
95103
consistent::TriState = e.consistent,
96104
effect_free::TriState = e.effect_free,
97105
nothrow::TriState = e.nothrow,
98106
terminates::TriState = e.terminates,
99107
nonoverlayed::Bool = e.nonoverlayed,
108+
notaskstate::TriState = e.notaskstate,
100109
inbounds_taints_consistency::Bool = e.inbounds_taints_consistency)
101110
return Effects(
102111
consistent,
103112
effect_free,
104113
nothrow,
105114
terminates,
106115
nonoverlayed,
116+
notaskstate,
107117
inbounds_taints_consistency)
108118
end
109119

110120
is_consistent(effects::Effects) = effects.consistent === ALWAYS_TRUE
111121
is_effect_free(effects::Effects) = effects.effect_free === ALWAYS_TRUE
112122
is_nothrow(effects::Effects) = effects.nothrow === ALWAYS_TRUE
113123
is_terminates(effects::Effects) = effects.terminates === ALWAYS_TRUE
124+
is_notaskstate(effects::Effects) = effects.notaskstate === ALWAYS_TRUE
114125
is_nonoverlayed(effects::Effects) = effects.nonoverlayed
115126

116127
is_concrete_eval_eligible(effects::Effects) =
@@ -132,7 +143,8 @@ function encode_effects(e::Effects)
132143
(e.effect_free.state << 2) |
133144
(e.nothrow.state << 4) |
134145
(e.terminates.state << 6) |
135-
(UInt32(e.nonoverlayed) << 8)
146+
(UInt32(e.nonoverlayed) << 8) |
147+
(UInt32(e.notaskstate.state) << 9)
136148
end
137149
function decode_effects(e::UInt32)
138150
return Effects(
@@ -141,6 +153,7 @@ function decode_effects(e::UInt32)
141153
TriState((e >> 4) & 0x03),
142154
TriState((e >> 6) & 0x03),
143155
_Bool( (e >> 8) & 0x01),
156+
TriState((e >> 9) & 0x03),
144157
false)
145158
end
146159

@@ -155,6 +168,8 @@ function tristate_merge(old::Effects, new::Effects)
155168
tristate_merge(
156169
old.terminates, new.terminates),
157170
old.nonoverlayed & new.nonoverlayed,
171+
tristate_merge(
172+
old.notaskstate, new.notaskstate),
158173
old.inbounds_taints_consistency | new.inbounds_taints_consistency)
159174
end
160175

0 commit comments

Comments
 (0)