Skip to content

Commit 8657232

Browse files
committed
extend :nonoverlayed effect bit and add new :consistent_overlay override
1 parent df5cb64 commit 8657232

File tree

13 files changed

+136
-84
lines changed

13 files changed

+136
-84
lines changed

base/boot.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ macro _foldable_meta()
284284
#=:inaccessiblememonly=#true,
285285
#=:noub=#true,
286286
#=:noub_if_noinbounds=#false,
287-
#=:nonoverlayed=#false))
287+
#=:consistent_overlay=#false))
288288
end
289289

290290
macro inline() Expr(:meta, :inline) end

base/compiler/abstractinterpretation.jl

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ function add_call_backedges!(interp::AbstractInterpreter, @nospecialize(rettype)
501501
# ignore the `:nonoverlayed` property if `interp` doesn't use overlayed method table
502502
# since it will never be tainted anyway
503503
if !isoverlayed(method_table(interp))
504-
all_effects = Effects(all_effects; nonoverlayed=false)
504+
all_effects = Effects(all_effects; nonoverlayed=ALWAYS_FALSE)
505505
end
506506
all_effects === Effects() && return nothing
507507
end
@@ -903,7 +903,15 @@ function concrete_eval_eligible(interp::AbstractInterpreter,
903903
mi = result.edge
904904
if mi !== nothing && is_foldable(effects)
905905
if f !== nothing && is_all_const_arg(arginfo, #=start=#2)
906-
if is_nonoverlayed(interp) || is_nonoverlayed(effects)
906+
if (is_nonoverlayed(interp) || is_nonoverlayed(effects) ||
907+
# Even if overlay methods are involved, when `:consistent_overlay` is
908+
# explicitly applied, we can still perform concrete evaluation using the
909+
# original methods for executing them.
910+
# While there's a chance that the non-overlayed counterparts may raise
911+
# non-egal exceptions, it will not impact the compilation validity, since:
912+
# - the results of the concrete evaluation will not be inlined
913+
# - the exception types from the concrete evaluation will not be propagated
914+
is_consistent_overlay(effects))
907915
return :concrete_eval
908916
end
909917
# disable concrete-evaluation if this function call is tainted by some overlayed
@@ -2815,9 +2823,8 @@ function override_effects(effects::Effects, override::EffectsOverride)
28152823
notaskstate = override.notaskstate ? true : effects.notaskstate,
28162824
inaccessiblememonly = override.inaccessiblememonly ? ALWAYS_TRUE : effects.inaccessiblememonly,
28172825
noub = override.noub ? ALWAYS_TRUE :
2818-
override.noub_if_noinbounds && effects.noub !== ALWAYS_TRUE ? NOUB_IF_NOINBOUNDS :
2819-
effects.noub,
2820-
nonoverlayed = override.nonoverlayed ? true : effects.nonoverlayed)
2826+
(override.noub_if_noinbounds && effects.noub !== ALWAYS_TRUE) ? NOUB_IF_NOINBOUNDS :
2827+
effects.noub)
28212828
end
28222829

28232830
isdefined_globalref(g::GlobalRef) = !iszero(ccall(:jl_globalref_boundp, Cint, (Any,), g))

base/compiler/compiler.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ struct EffectsOverride
4747
inaccessiblememonly::Bool
4848
noub::Bool
4949
noub_if_noinbounds::Bool
50-
nonoverlayed::Bool
50+
consistent_overlay::Bool
5151
end
5252
function EffectsOverride(
5353
override::EffectsOverride =
@@ -61,7 +61,7 @@ function EffectsOverride(
6161
inaccessiblememonly::Bool = override.inaccessiblememonly,
6262
noub::Bool = override.noub,
6363
noub_if_noinbounds::Bool = override.noub_if_noinbounds,
64-
nonoverlayed::Bool = override.nonoverlayed)
64+
consistent_overlay::Bool = override.consistent_overlay)
6565
return EffectsOverride(
6666
consistent,
6767
effect_free,
@@ -72,7 +72,7 @@ function EffectsOverride(
7272
inaccessiblememonly,
7373
noub,
7474
noub_if_noinbounds,
75-
nonoverlayed)
75+
consistent_overlay)
7676
end
7777
const NUM_EFFECTS_OVERRIDES = 10 # sync with julia.h
7878

base/compiler/effects.jl

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,21 @@ following meanings:
4343
except that it may access or modify mutable memory pointed to by its call arguments.
4444
This may later be refined to `ALWAYS_TRUE` in a case when call arguments are known to be immutable.
4545
This state corresponds to LLVM's `inaccessiblemem_or_argmemonly` function attribute.
46-
- `noub::UInt8`: indicates that the method will not execute any undefined behavior (for any input).
47-
Note that undefined behavior may technically cause the method to violate any other effect
48-
assertions (such as `:consistent` or `:effect_free`) as well, but we do not model this,
49-
and they assume the absence of undefined behavior.
50-
* `ALWAYS_TRUE`: this method is guaranteed to not execute any undefined behavior.
46+
- `noub::UInt8`:
47+
* `ALWAYS_TRUE`: this method is guaranteed to not execute any undefined behavior (for any input).
5148
* `ALWAYS_FALSE`: this method may execute undefined behavior.
5249
* `NOUB_IF_NOINBOUNDS`: this method is guaranteed to not execute any undefined behavior
5350
if the caller does not set nor propagate the `@inbounds` context.
54-
- `nonoverlayed::Bool`: indicates that any methods that may be called within this method
55-
are not defined in an [overlayed method table](@ref OverlayMethodTable).
51+
Note that undefined behavior may technically cause the method to violate any other effect
52+
assertions (such as `:consistent` or `:effect_free`) as well, but we do not model this,
53+
and they assume the absence of undefined behavior.
54+
- `nonoverlayed::UInt8`:
55+
* `ALWAYS_TRUE`: this method is guaranteed to not invoke any methods that defined in an
56+
[overlayed method table](@ref OverlayMethodTable).
57+
* `CONSISTENT_OVERLAY`: this method may invoke overlayed methods, but all such overlayed
58+
methods are `:consistent` with their non-overlayed original counterparts
59+
(see [`Base.@assume_effects`](@ref) for the exact definition of `:consistenct`-cy).
60+
* `ALWAYS_FALSE`: this method may invoke overlayed methods.
5661
5762
Note that the representations above are just internal implementation details and thus likely
5863
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
9499
- `+u` (green): `true`
95100
- `-u` (red): `false`
96101
- `?u` (yellow): `NOUB_IF_NOINBOUNDS`
97-
98-
Additionally, if the `nonoverlayed` property is false, a red prime symbol (′) is displayed after the tuple.
102+
8. `:nonoverlayed` (`o`):
103+
- `+o` (green): `ALWAYS_TRUE`
104+
- `-o` (red): `ALWAYS_FALSE`
105+
- `?o` (yellow): `CONSISTENT_OVERLAY`
99106
"""
100107
struct Effects
101108
consistent::UInt8
@@ -105,7 +112,7 @@ struct Effects
105112
notaskstate::Bool
106113
inaccessiblememonly::UInt8
107114
noub::UInt8
108-
nonoverlayed::Bool
115+
nonoverlayed::UInt8
109116
function Effects(
110117
consistent::UInt8,
111118
effect_free::UInt8,
@@ -114,7 +121,7 @@ struct Effects
114121
notaskstate::Bool,
115122
inaccessiblememonly::UInt8,
116123
noub::UInt8,
117-
nonoverlayed::Bool)
124+
nonoverlayed::UInt8)
118125
return new(
119126
consistent,
120127
effect_free,
@@ -150,10 +157,13 @@ const INACCESSIBLEMEM_OR_ARGMEMONLY = 0x01 << 1
150157
# :noub bits
151158
const NOUB_IF_NOINBOUNDS = 0x01 << 1
152159

153-
const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, true)
154-
const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, true)
155-
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)
156-
const _EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, false) # unknown really
160+
# :nonoverlayed bits
161+
const CONSISTENT_OVERLAY = 0x01 << 1
162+
163+
const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE)
164+
const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE)
165+
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)
166+
const _EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_FALSE) # unknown really
157167

158168
function Effects(effects::Effects = _EFFECTS_UNKNOWN;
159169
consistent::UInt8 = effects.consistent,
@@ -163,7 +173,7 @@ function Effects(effects::Effects = _EFFECTS_UNKNOWN;
163173
notaskstate::Bool = effects.notaskstate,
164174
inaccessiblememonly::UInt8 = effects.inaccessiblememonly,
165175
noub::UInt8 = effects.noub,
166-
nonoverlayed::Bool = effects.nonoverlayed)
176+
nonoverlayed::UInt8 = effects.nonoverlayed)
167177
return Effects(
168178
consistent,
169179
effect_free,
@@ -229,8 +239,11 @@ function is_better_effects(new::Effects, old::Effects)
229239
elseif new.noub != old.noub
230240
return false
231241
end
232-
if new.nonoverlayed
233-
any_improved |= !old.nonoverlayed
242+
if new.nonoverlayed == ALWAYS_TRUE
243+
any_improved |= old.nonoverlayed != ALWAYS_TRUE
244+
elseif new.nonoverlayed == CONSISTENT_OVERLAY
245+
old.nonoverlayed == ALWAYS_TRUE && return false
246+
any_improved |= old.nonoverlayed != CONSISTENT_OVERLAY
234247
elseif new.nonoverlayed != old.nonoverlayed
235248
return false
236249
end
@@ -265,7 +278,7 @@ is_notaskstate(effects::Effects) = effects.notaskstate
265278
is_inaccessiblememonly(effects::Effects) = effects.inaccessiblememonly === ALWAYS_TRUE
266279
is_noub(effects::Effects) = effects.noub === ALWAYS_TRUE
267280
is_noub_if_noinbounds(effects::Effects) = effects.noub === NOUB_IF_NOINBOUNDS
268-
is_nonoverlayed(effects::Effects) = effects.nonoverlayed
281+
is_nonoverlayed(effects::Effects) = effects.nonoverlayed === ALWAYS_TRUE
269282

270283
# implies `is_notaskstate` & `is_inaccessiblememonly`, but not explicitly checked here
271284
is_foldable(effects::Effects) =
@@ -295,6 +308,8 @@ is_effect_free_if_inaccessiblememonly(effects::Effects) = !iszero(effects.effect
295308

296309
is_inaccessiblemem_or_argmemonly(effects::Effects) = effects.inaccessiblememonly === INACCESSIBLEMEM_OR_ARGMEMONLY
297310

311+
is_consistent_overlay(effects::Effects) = effects.nonoverlayed === CONSISTENT_OVERLAY
312+
298313
function encode_effects(e::Effects)
299314
return ((e.consistent % UInt32) << 0) |
300315
((e.effect_free % UInt32) << 3) |
@@ -315,7 +330,7 @@ function decode_effects(e::UInt32)
315330
_Bool((e >> 7) & 0x01),
316331
UInt8((e >> 8) & 0x03),
317332
UInt8((e >> 10) & 0x03),
318-
_Bool((e >> 12) & 0x01))
333+
UInt8((e >> 12) & 0x03))
319334
end
320335

321336
function encode_effects_override(eo::EffectsOverride)
@@ -329,7 +344,7 @@ function encode_effects_override(eo::EffectsOverride)
329344
eo.inaccessiblememonly && (e |= (0x0001 << 6))
330345
eo.noub && (e |= (0x0001 << 7))
331346
eo.noub_if_noinbounds && (e |= (0x0001 << 8))
332-
eo.nonoverlayed && (e |= (0x0001 << 9))
347+
eo.consistent_overlay && (e |= (0x0001 << 9))
333348
return e
334349
end
335350

base/compiler/inferencestate.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,10 @@ mutable struct InferenceState
336336
end
337337

338338
if def isa Method
339-
ipo_effects = Effects(ipo_effects; nonoverlayed=is_nonoverlayed(def))
339+
nonoverlayed = is_nonoverlayed(def) ? ALWAYS_TRUE :
340+
is_effect_overridden(def, :consistent_overlay) ? CONSISTENT_OVERLAY :
341+
ALWAYS_FALSE
342+
ipo_effects = Effects(ipo_effects; nonoverlayed)
340343
end
341344

342345
restrict_abstract_call_sites = isa(def, Module)

base/compiler/ssair/show.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1048,8 +1048,9 @@ function Base.show(io::IO, e::Effects)
10481048
printstyled(io, effectbits_letter(e, :inaccessiblememonly, 'm'); color=effectbits_color(e, :inaccessiblememonly))
10491049
print(io, ',')
10501050
printstyled(io, effectbits_letter(e, :noub, 'u'); color=effectbits_color(e, :noub))
1051+
print(io, ',')
1052+
printstyled(io, effectbits_letter(e, :nonoverlayed, 'o'); color=effectbits_color(e, :nonoverlayed))
10511053
print(io, ')')
1052-
e.nonoverlayed || printstyled(io, ''; color=:red)
10531054
end
10541055

10551056
@specialize

base/compiler/typeinfer.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -448,8 +448,8 @@ function adjust_effects(ipo_effects::Effects, def::Method)
448448
elseif is_effect_overridden(override, :noub_if_noinbounds) && ipo_effects.noub !== ALWAYS_TRUE
449449
ipo_effects = Effects(ipo_effects; noub=NOUB_IF_NOINBOUNDS)
450450
end
451-
if is_effect_overridden(override, :nonoverlayed)
452-
ipo_effects = Effects(ipo_effects; nonoverlayed=true)
451+
if is_effect_overridden(override, :consistent_overlay)
452+
ipo_effects = Effects(ipo_effects; nonoverlayed=CONSISTENT_OVERLAY)
453453
end
454454
return ipo_effects
455455
end

base/essentials.jl

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ macro _total_meta()
202202
#=:inaccessiblememonly=#true,
203203
#=:noub=#true,
204204
#=:noub_if_noinbounds=#false,
205-
#=:nonoverlayed=#false))
205+
#=:consistent_overlay=#false))
206206
end
207207
# can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping)
208208
macro _foldable_meta()
@@ -216,7 +216,7 @@ macro _foldable_meta()
216216
#=:inaccessiblememonly=#true,
217217
#=:noub=#true,
218218
#=:noub_if_noinbounds=#false,
219-
#=:nonoverlayed=#false))
219+
#=:consistent_overlay=#false))
220220
end
221221
# can be used in place of `@assume_effects :terminates_locally` (supposed to be used for bootstrapping)
222222
macro _terminates_locally_meta()
@@ -230,7 +230,7 @@ macro _terminates_locally_meta()
230230
#=:inaccessiblememonly=#false,
231231
#=:noub=#false,
232232
#=:noub_if_noinbounds=#false,
233-
#=:nonoverlayed=#false))
233+
#=:consistent_overlay=#false))
234234
end
235235
# can be used in place of `@assume_effects :terminates_globally` (supposed to be used for bootstrapping)
236236
macro _terminates_globally_meta()
@@ -244,7 +244,7 @@ macro _terminates_globally_meta()
244244
#=:inaccessiblememonly=#false,
245245
#=:noub=#false,
246246
#=:noub_if_noinbounds=#false,
247-
#=:nonoverlayed=#false))
247+
#=:consistent_overlay=#false))
248248
end
249249
# can be used in place of `@assume_effects :terminates_globally :notaskstate` (supposed to be used for bootstrapping)
250250
macro _terminates_globally_notaskstate_meta()
@@ -258,7 +258,7 @@ macro _terminates_globally_notaskstate_meta()
258258
#=:inaccessiblememonly=#false,
259259
#=:noub=#false,
260260
#=:noub_if_noinbounds=#false,
261-
#=:nonoverlayed=#false))
261+
#=:consistent_overlay=#false))
262262
end
263263
# can be used in place of `@assume_effects :terminates_globally :noub` (supposed to be used for bootstrapping)
264264
macro _terminates_globally_noub_meta()
@@ -272,7 +272,7 @@ macro _terminates_globally_noub_meta()
272272
#=:inaccessiblememonly=#false,
273273
#=:noub=#true,
274274
#=:noub_if_noinbounds=#false,
275-
#=:nonoverlayed=#false))
275+
#=:consistent_overlay=#false))
276276
end
277277
# can be used in place of `@assume_effects :effect_free :terminates_locally` (supposed to be used for bootstrapping)
278278
macro _effect_free_terminates_locally_meta()
@@ -286,7 +286,7 @@ macro _effect_free_terminates_locally_meta()
286286
#=:inaccessiblememonly=#false,
287287
#=:noub=#false,
288288
#=:noub_if_noinbounds=#false,
289-
#=:nonoverlayed=#false))
289+
#=:consistent_overlay=#false))
290290
end
291291
# can be used in place of `@assume_effects :nothrow :noub` (supposed to be used for bootstrapping)
292292
macro _nothrow_noub_meta()
@@ -300,7 +300,7 @@ macro _nothrow_noub_meta()
300300
#=:inaccessiblememonly=#false,
301301
#=:noub=#true,
302302
#=:noub_if_noinbounds=#false,
303-
#=:nonoverlayed=#false))
303+
#=:consistent_overlay=#false))
304304
end
305305
# can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping)
306306
macro _nothrow_meta()
@@ -314,7 +314,7 @@ macro _nothrow_meta()
314314
#=:inaccessiblememonly=#false,
315315
#=:noub=#false,
316316
#=:noub_if_noinbounds=#false,
317-
#=:nonoverlayed=#false))
317+
#=:consistent_overlay=#false))
318318
end
319319
# can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping)
320320
macro _noub_meta()
@@ -328,7 +328,7 @@ macro _noub_meta()
328328
#=:inaccessiblememonly=#false,
329329
#=:noub=#true,
330330
#=:noub_if_noinbounds=#false,
331-
#=:nonoverlayed=#false))
331+
#=:consistent_overlay=#false))
332332
end
333333
# can be used in place of `@assume_effects :notaskstate` (supposed to be used for bootstrapping)
334334
macro _notaskstate_meta()
@@ -342,7 +342,7 @@ macro _notaskstate_meta()
342342
#=:inaccessiblememonly=#false,
343343
#=:noub=#false,
344344
#=:noub_if_noinbounds=#false,
345-
#=:nonoverlayed=#false))
345+
#=:consistent_overlay=#false))
346346
end
347347
# can be used in place of `@assume_effects :noub_if_noinbounds` (supposed to be used for bootstrapping)
348348
macro _noub_if_noinbounds_meta()
@@ -356,7 +356,7 @@ macro _noub_if_noinbounds_meta()
356356
#=:inaccessiblememonly=#false,
357357
#=:noub=#false,
358358
#=:noub_if_noinbounds=#true,
359-
#=:nonoverlayed=#false))
359+
#=:consistent_overlay=#false))
360360
end
361361

362362
# another version of inlining that propagates an inbounds context

0 commit comments

Comments
 (0)