Skip to content

Commit e25433c

Browse files
authored
Merge pull request #48075 from JuliaLang/backports-release-1.9
Backports for julia 1.9.0-beta3
2 parents 7daffee + 05d3871 commit e25433c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+908
-345
lines changed

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ Language changes
2525
Compiler/Runtime improvements
2626
-----------------------------
2727

28+
* Time to first execution (TTFX, sometimes called time to first plot) is greatly reduced. Package precompilation now
29+
saves native code into a "pkgimage", meaning that code generated during the precompilation process will not
30+
require compilation after package load. Use of pkgimages can be disabled via `--pkgimages=no` ([#44527]) ([#47184]).
2831
* The known quadratic behavior of type inference is now fixed and inference uses less memory in general.
2932
Certain edge cases with auto-generated long functions (e.g. ModelingToolkit.jl with partial
3033
differential equations and large causal models) should see significant compile-time improvements ([#45276], [#45404]).

base/Base.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,10 @@ for m in methods(include)
447447
delete_method(m)
448448
end
449449

450+
# This method is here only to be overwritten during the test suite to test
451+
# various sysimg related invalidation scenarios.
452+
a_method_to_overwrite_in_test() = inferencebarrier(1)
453+
450454
# These functions are duplicated in client.jl/include(::String) for
451455
# nicer stacktraces. Modifications here have to be backported there
452456
include(mod::Module, _path::AbstractString) = _include(identity, mod, _path)

base/compiler/abstractinterpretation.jl

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -781,9 +781,18 @@ end
781781
# - false: eligible for semi-concrete evaluation
782782
# - nothing: not eligible for either of it
783783
function concrete_eval_eligible(interp::AbstractInterpreter,
784-
@nospecialize(f), result::MethodCallResult, arginfo::ArgInfo)
784+
@nospecialize(f), result::MethodCallResult, arginfo::ArgInfo, sv::InferenceState)
785785
# disable all concrete-evaluation if this function call is tainted by some overlayed
786786
# method since currently there is no direct way to execute overlayed methods
787+
if inbounds_option() === :off
788+
# Disable concrete evaluation in `--check-bounds=no` mode, since we cannot be sure
789+
# that inferred effects are accurate.
790+
return nothing
791+
elseif !result.effects.noinbounds && stmt_taints_inbounds_consistency(sv)
792+
# If the current statement is @inbounds or we propagate inbounds, the call's consistency
793+
# is tainted and not consteval eligible.
794+
return nothing
795+
end
787796
isoverlayed(method_table(interp)) && !is_nonoverlayed(result.effects) && return nothing
788797
if f !== nothing && result.edge !== nothing && is_foldable(result.effects)
789798
if is_all_const_arg(arginfo, #=start=#2)
@@ -824,7 +833,7 @@ end
824833
function concrete_eval_call(interp::AbstractInterpreter,
825834
@nospecialize(f), result::MethodCallResult, arginfo::ArgInfo, si::StmtInfo,
826835
sv::InferenceState, invokecall::Union{Nothing,InvokeCall}=nothing)
827-
eligible = concrete_eval_eligible(interp, f, result, arginfo)
836+
eligible = concrete_eval_eligible(interp, f, result, arginfo, sv)
828837
eligible === nothing && return false
829838
if eligible
830839
args = collect_const_args(arginfo, #=start=#2)
@@ -2026,22 +2035,32 @@ function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::V
20262035
nothing
20272036
end
20282037

2029-
function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, sv::Union{InferenceState, IRCode})
2038+
function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable, Nothing}, sv::Union{InferenceState, IRCode})
2039+
rt = Any
20302040
head = e.head
20312041
if head === :static_parameter
20322042
n = e.args[1]::Int
2033-
t = Any
20342043
if 1 <= n <= length(sv.sptypes)
2035-
t = sv.sptypes[n]
2044+
rt = sv.sptypes[n]
20362045
end
2037-
return t
20382046
elseif head === :boundscheck
2039-
return Bool
2047+
if isa(sv, InferenceState)
2048+
flag = sv.src.ssaflags[sv.currpc]
2049+
# If there is no particular @inbounds for this function, then we only taint `noinbounds`,
2050+
# which will subsequently taint consistency if this function is called from another
2051+
# function that uses `@inbounds`. However, if this :boundscheck is itself within an
2052+
# `@inbounds` region, its value depends on `--check-bounds`, so we need to taint
2053+
# consistency here also.
2054+
merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; noinbounds=false,
2055+
consistent = (flag & IR_FLAG_INBOUNDS) != 0 ? ALWAYS_FALSE : ALWAYS_TRUE))
2056+
end
2057+
rt = Bool
2058+
elseif head === :inbounds
2059+
@assert false && "Expected this to have been moved into flags"
20402060
elseif head === :the_exception
20412061
merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; consistent=ALWAYS_FALSE))
2042-
return Any
20432062
end
2044-
return Any
2063+
return rt
20452064
end
20462065

20472066
function abstract_eval_special_value(interp::AbstractInterpreter, @nospecialize(e), vtypes::Union{VarTable, Nothing}, sv::Union{InferenceState, IRCode})
@@ -2071,7 +2090,7 @@ end
20712090

20722091
function abstract_eval_value(interp::AbstractInterpreter, @nospecialize(e), vtypes::Union{VarTable, Nothing}, sv::Union{InferenceState, IRCode})
20732092
if isa(e, Expr)
2074-
return abstract_eval_value_expr(interp, e, sv)
2093+
return abstract_eval_value_expr(interp, e, vtypes, sv)
20752094
else
20762095
typ = abstract_eval_special_value(interp, e, vtypes, sv)
20772096
return collect_limitations!(typ, sv)
@@ -2112,7 +2131,6 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
21122131
arginfo = ArgInfo(ea, argtypes)
21132132
si = StmtInfo(isa(sv, IRCode) ? true : !call_result_unused(sv, sv.currpc))
21142133
(; rt, effects, info) = abstract_call(interp, arginfo, si, sv)
2115-
merge_effects!(interp, sv, effects)
21162134
if isa(sv, InferenceState)
21172135
sv.stmt_info[sv.currpc] = info
21182136
end
@@ -2176,7 +2194,6 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
21762194
nothrow = false
21772195
end
21782196
effects = Effects(EFFECTS_TOTAL; consistent, nothrow)
2179-
merge_effects!(interp, sv, effects)
21802197
elseif ehead === :splatnew
21812198
t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv))
21822199
nothrow = false # TODO: More precision
@@ -2195,7 +2212,6 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
21952212
end
21962213
consistent = !ismutabletype(t) ? ALWAYS_TRUE : CONSISTENT_IF_NOTRETURNED
21972214
effects = Effects(EFFECTS_TOTAL; consistent, nothrow)
2198-
merge_effects!(interp, sv, effects)
21992215
elseif ehead === :new_opaque_closure
22002216
t = Union{}
22012217
effects = Effects() # TODO
@@ -2223,20 +2239,16 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
22232239
elseif ehead === :foreigncall
22242240
(;rt, effects) = abstract_eval_foreigncall(interp, e, vtypes, sv, mi)
22252241
t = rt
2226-
merge_effects!(interp, sv, effects)
22272242
elseif ehead === :cfunction
22282243
effects = EFFECTS_UNKNOWN
2229-
merge_effects!(interp, sv, effects)
22302244
t = e.args[1]
22312245
isa(t, Type) || (t = Any)
22322246
abstract_eval_cfunction(interp, e, vtypes, sv)
22332247
elseif ehead === :method
22342248
t = (length(e.args) == 1) ? Any : Nothing
22352249
effects = EFFECTS_UNKNOWN
2236-
merge_effects!(interp, sv, effects)
22372250
elseif ehead === :copyast
22382251
effects = EFFECTS_UNKNOWN
2239-
merge_effects!(interp, sv, effects)
22402252
t = abstract_eval_value(interp, e.args[1], vtypes, sv)
22412253
if t isa Const && t.val isa Expr
22422254
# `copyast` makes copies of Exprs
@@ -2247,6 +2259,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
22472259
elseif ehead === :isdefined
22482260
sym = e.args[1]
22492261
t = Bool
2262+
effects = EFFECTS_TOTAL
22502263
if isa(sym, SlotNumber)
22512264
vtyp = vtypes[slot_id(sym)]
22522265
if vtyp.typ === Bottom
@@ -2275,9 +2288,9 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
22752288
@label always_throw
22762289
t = Bottom
22772290
effects = EFFECTS_THROWS
2278-
merge_effects!(interp, sv, effects)
22792291
else
2280-
t = abstract_eval_value_expr(interp, e, sv)
2292+
t = abstract_eval_value_expr(interp, e, vtypes, sv)
2293+
effects = EFFECTS_TOTAL
22812294
end
22822295
return RTEffects(t, effects)
22832296
end
@@ -2319,6 +2332,11 @@ function abstract_eval_phi(interp::AbstractInterpreter, phi::PhiNode, vtypes::Un
23192332
return rt
23202333
end
23212334

2335+
function stmt_taints_inbounds_consistency(sv::InferenceState)
2336+
flag = sv.src.ssaflags[sv.currpc]
2337+
return sv.src.propagate_inbounds || (flag & IR_FLAG_INBOUNDS) != 0
2338+
end
2339+
23222340
function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState)
23232341
if !isa(e, Expr)
23242342
if isa(e, PhiNode)
@@ -2327,6 +2345,18 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e),
23272345
return abstract_eval_special_value(interp, e, vtypes, sv)
23282346
end
23292347
(;rt, effects) = abstract_eval_statement_expr(interp, e, vtypes, sv, nothing)
2348+
if !effects.noinbounds
2349+
flag = sv.src.ssaflags[sv.currpc]
2350+
if !sv.src.propagate_inbounds
2351+
# The callee read our inbounds flag, but unless we propagate inbounds,
2352+
# we ourselves don't read our parent's inbounds.
2353+
effects = Effects(effects; noinbounds=true)
2354+
end
2355+
if (flag & IR_FLAG_INBOUNDS) != 0
2356+
effects = Effects(effects; consistent=ALWAYS_FALSE)
2357+
end
2358+
end
2359+
merge_effects!(interp, sv, effects)
23302360
e = e::Expr
23312361
@assert !isa(rt, TypeVar) "unhandled TypeVar"
23322362
rt = maybe_singleton_const(rt)

base/compiler/effects.jl

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ following meanings:
4040
This state corresponds to LLVM's `inaccessiblemem_or_argmemonly` function attribute.
4141
- `nonoverlayed::Bool`: indicates that any methods that may be called within this method
4242
are not defined in an [overlayed method table](@ref OverlayMethodTable).
43-
- `noinbounds::Bool`: indicates this method can't be `:consistent` because of bounds checking.
44-
This effect is currently only set on `InferenceState` construction and used to taint
45-
`:consistent`-cy before caching. We may want to track it with more accuracy in the future.
43+
- `noinbounds::Bool`: If set, indicates that this method does not read the parent's :inbounds
44+
state. In particular, it does not have any reached :boundscheck exprs, not propagates inbounds
45+
to any children that do.
4646
4747
Note that the representations above are just internal implementation details and thus likely
4848
to change in the future. See [`Base.@assume_effects`](@ref) for more detailed explanation
@@ -98,10 +98,10 @@ const EFFECT_FREE_IF_INACCESSIBLEMEMONLY = 0x01 << 1
9898
# :inaccessiblememonly bits
9999
const INACCESSIBLEMEM_OR_ARGMEMONLY = 0x01 << 1
100100

101-
const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, true)
102-
const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, true)
103-
const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, true) # unknown mostly, but it's not overlayed at least (e.g. it's not a call)
104-
const EFFECTS_UNKNOWN′ = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, false) # unknown really
101+
const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, true, true)
102+
const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, true, true)
103+
const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, true, true) # unknown mostly, but it's not overlayed at least (e.g. it's not a call)
104+
const EFFECTS_UNKNOWN′ = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, false, true) # unknown really
105105

106106
function Effects(e::Effects = EFFECTS_UNKNOWN′;
107107
consistent::UInt8 = e.consistent,
@@ -184,7 +184,8 @@ function encode_effects(e::Effects)
184184
((e.terminates % UInt32) << 6) |
185185
((e.notaskstate % UInt32) << 7) |
186186
((e.inaccessiblememonly % UInt32) << 8) |
187-
((e.nonoverlayed % UInt32) << 10)
187+
((e.nonoverlayed % UInt32) << 10)|
188+
((e.noinbounds % UInt32) << 11)
188189
end
189190

190191
function decode_effects(e::UInt32)
@@ -195,7 +196,8 @@ function decode_effects(e::UInt32)
195196
_Bool((e >> 6) & 0x01),
196197
_Bool((e >> 7) & 0x01),
197198
UInt8((e >> 8) & 0x03),
198-
_Bool((e >> 10) & 0x01))
199+
_Bool((e >> 10) & 0x01),
200+
_Bool((e >> 11) & 0x01))
199201
end
200202

201203
struct EffectsOverride

base/compiler/inferencestate.jl

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -177,16 +177,7 @@ mutable struct InferenceState
177177

178178
valid_worlds = WorldRange(src.min_world, src.max_world == typemax(UInt) ? get_world_counter() : src.max_world)
179179
bestguess = Bottom
180-
# TODO: Currently, any :inbounds declaration taints consistency,
181-
# because we cannot be guaranteed whether or not boundschecks
182-
# will be eliminated and if they are, we cannot be guaranteed
183-
# that no undefined behavior will occur (the effects assumptions
184-
# are stronger than the inbounds assumptions, since the latter
185-
# requires dynamic reachability, while the former is global).
186-
inbounds = inbounds_option()
187-
noinbounds = inbounds === :on || (inbounds === :default && !any_inbounds(code))
188-
consistent = noinbounds ? ALWAYS_TRUE : ALWAYS_FALSE
189-
ipo_effects = Effects(EFFECTS_TOTAL; consistent, noinbounds)
180+
ipo_effects = Effects(EFFECTS_TOTAL)
190181

191182
params = InferenceParams(interp)
192183
restrict_abstract_call_sites = isa(linfo.def, Module)
@@ -240,16 +231,6 @@ function bail_out_apply(::AbstractInterpreter, @nospecialize(rt), sv::Union{Infe
240231
return rt === Any
241232
end
242233

243-
function any_inbounds(code::Vector{Any})
244-
for i = 1:length(code)
245-
stmt = code[i]
246-
if isexpr(stmt, :inbounds)
247-
return true
248-
end
249-
end
250-
return false
251-
end
252-
253234
was_reached(sv::InferenceState, pc::Int) = sv.ssavaluetypes[pc] !== NOT_FOUND
254235

255236
function compute_trycatch(code::Vector{Any}, ip::BitSet)

base/compiler/ssair/show.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -858,7 +858,7 @@ function show_ir_stmts(io::IO, ir::Union{IRCode, CodeInfo, IncrementalCompact},
858858
return bb_idx
859859
end
860860

861-
function finish_show_ir(io::IO, cfg, config::IRShowConfig)
861+
function finish_show_ir(io::IO, cfg::CFG, config::IRShowConfig)
862862
max_bb_idx_size = length(string(length(cfg.blocks)))
863863
config.line_info_preprinter(io, " "^(max_bb_idx_size + 2), 0)
864864
return nothing
@@ -1012,6 +1012,8 @@ function Base.show(io::IO, e::Effects)
10121012
printstyled(io, effectbits_letter(e, :notaskstate, 's'); color=effectbits_color(e, :notaskstate))
10131013
print(io, ',')
10141014
printstyled(io, effectbits_letter(e, :inaccessiblememonly, 'm'); color=effectbits_color(e, :inaccessiblememonly))
1015+
print(io, ',')
1016+
printstyled(io, effectbits_letter(e, :noinbounds, 'i'); color=effectbits_color(e, :noinbounds))
10151017
print(io, ')')
10161018
e.nonoverlayed || printstyled(io, ''; color=:red)
10171019
end

base/compiler/tfuncs.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2039,7 +2039,7 @@ function getfield_effects(argtypes::Vector{Any}, @nospecialize(rt))
20392039
# If we cannot independently prove inboundsness, taint consistency.
20402040
# The inbounds-ness assertion requires dynamic reachability, while
20412041
# :consistent needs to be true for all input values.
2042-
# N.B. We do not taint for `--check-bounds=no` here -that happens in
2042+
# N.B. We do not taint for `--check-bounds=no` here that happens in
20432043
# InferenceState.
20442044
if length(argtypes) 2 && getfield_nothrow(argtypes[1], argtypes[2], true)
20452045
nothrow = true

base/deprecated.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,6 @@ const var"@_noinline_meta" = var"@noinline"
318318

319319
# BEGIN 1.9 deprecations
320320

321-
@deprecate splat(x) Splat(x) false
322-
323321
# We'd generally like to avoid direct external access to internal fields
324322
# Core.Compiler.is_inlineable and Core.Compiler.set_inlineable! move towards this direction,
325323
# but we need to keep these around for compat

base/exports.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,7 @@ export
816816
atreplinit,
817817
exit,
818818
ntuple,
819-
Splat,
819+
splat,
820820

821821
# I/O and events
822822
close,

base/expr.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,10 @@ currently equivalent to the following `setting`s:
607607
however, that by the `:consistent`-cy requirements, any such annotated call
608608
must consistently throw given the same argument values.
609609
610+
!!! note
611+
An explict `@inbounds` annotation inside the function will also disable
612+
constant propagation and not be overriden by :foldable.
613+
610614
---
611615
## `:total`
612616

0 commit comments

Comments
 (0)