Skip to content

Commit

Permalink
Merge pull request #48075 from JuliaLang/backports-release-1.9
Browse files Browse the repository at this point in the history
Backports for julia 1.9.0-beta3
  • Loading branch information
KristofferC authored Jan 17, 2023
2 parents 7daffee + 05d3871 commit e25433c
Show file tree
Hide file tree
Showing 75 changed files with 908 additions and 345 deletions.
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ Language changes
Compiler/Runtime improvements
-----------------------------

* Time to first execution (TTFX, sometimes called time to first plot) is greatly reduced. Package precompilation now
saves native code into a "pkgimage", meaning that code generated during the precompilation process will not
require compilation after package load. Use of pkgimages can be disabled via `--pkgimages=no` ([#44527]) ([#47184]).
* The known quadratic behavior of type inference is now fixed and inference uses less memory in general.
Certain edge cases with auto-generated long functions (e.g. ModelingToolkit.jl with partial
differential equations and large causal models) should see significant compile-time improvements ([#45276], [#45404]).
Expand Down
4 changes: 4 additions & 0 deletions base/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,10 @@ for m in methods(include)
delete_method(m)
end

# This method is here only to be overwritten during the test suite to test
# various sysimg related invalidation scenarios.
a_method_to_overwrite_in_test() = inferencebarrier(1)

# These functions are duplicated in client.jl/include(::String) for
# nicer stacktraces. Modifications here have to be backported there
include(mod::Module, _path::AbstractString) = _include(identity, mod, _path)
Expand Down
68 changes: 49 additions & 19 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -781,9 +781,18 @@ end
# - false: eligible for semi-concrete evaluation
# - nothing: not eligible for either of it
function concrete_eval_eligible(interp::AbstractInterpreter,
@nospecialize(f), result::MethodCallResult, arginfo::ArgInfo)
@nospecialize(f), result::MethodCallResult, arginfo::ArgInfo, sv::InferenceState)
# disable all concrete-evaluation if this function call is tainted by some overlayed
# method since currently there is no direct way to execute overlayed methods
if inbounds_option() === :off
# Disable concrete evaluation in `--check-bounds=no` mode, since we cannot be sure
# that inferred effects are accurate.
return nothing
elseif !result.effects.noinbounds && stmt_taints_inbounds_consistency(sv)
# If the current statement is @inbounds or we propagate inbounds, the call's consistency
# is tainted and not consteval eligible.
return nothing
end
isoverlayed(method_table(interp)) && !is_nonoverlayed(result.effects) && return nothing
if f !== nothing && result.edge !== nothing && is_foldable(result.effects)
if is_all_const_arg(arginfo, #=start=#2)
Expand Down Expand Up @@ -824,7 +833,7 @@ end
function concrete_eval_call(interp::AbstractInterpreter,
@nospecialize(f), result::MethodCallResult, arginfo::ArgInfo, si::StmtInfo,
sv::InferenceState, invokecall::Union{Nothing,InvokeCall}=nothing)
eligible = concrete_eval_eligible(interp, f, result, arginfo)
eligible = concrete_eval_eligible(interp, f, result, arginfo, sv)
eligible === nothing && return false
if eligible
args = collect_const_args(arginfo, #=start=#2)
Expand Down Expand Up @@ -2026,22 +2035,32 @@ function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::V
nothing
end

function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, sv::Union{InferenceState, IRCode})
function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable, Nothing}, sv::Union{InferenceState, IRCode})
rt = Any
head = e.head
if head === :static_parameter
n = e.args[1]::Int
t = Any
if 1 <= n <= length(sv.sptypes)
t = sv.sptypes[n]
rt = sv.sptypes[n]
end
return t
elseif head === :boundscheck
return Bool
if isa(sv, InferenceState)
flag = sv.src.ssaflags[sv.currpc]
# If there is no particular @inbounds for this function, then we only taint `noinbounds`,
# which will subsequently taint consistency if this function is called from another
# function that uses `@inbounds`. However, if this :boundscheck is itself within an
# `@inbounds` region, its value depends on `--check-bounds`, so we need to taint
# consistency here also.
merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; noinbounds=false,
consistent = (flag & IR_FLAG_INBOUNDS) != 0 ? ALWAYS_FALSE : ALWAYS_TRUE))
end
rt = Bool
elseif head === :inbounds
@assert false && "Expected this to have been moved into flags"
elseif head === :the_exception
merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; consistent=ALWAYS_FALSE))
return Any
end
return Any
return rt
end

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

function abstract_eval_value(interp::AbstractInterpreter, @nospecialize(e), vtypes::Union{VarTable, Nothing}, sv::Union{InferenceState, IRCode})
if isa(e, Expr)
return abstract_eval_value_expr(interp, e, sv)
return abstract_eval_value_expr(interp, e, vtypes, sv)
else
typ = abstract_eval_special_value(interp, e, vtypes, sv)
return collect_limitations!(typ, sv)
Expand Down Expand Up @@ -2112,7 +2131,6 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
arginfo = ArgInfo(ea, argtypes)
si = StmtInfo(isa(sv, IRCode) ? true : !call_result_unused(sv, sv.currpc))
(; rt, effects, info) = abstract_call(interp, arginfo, si, sv)
merge_effects!(interp, sv, effects)
if isa(sv, InferenceState)
sv.stmt_info[sv.currpc] = info
end
Expand Down Expand Up @@ -2176,7 +2194,6 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
nothrow = false
end
effects = Effects(EFFECTS_TOTAL; consistent, nothrow)
merge_effects!(interp, sv, effects)
elseif ehead === :splatnew
t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv))
nothrow = false # TODO: More precision
Expand All @@ -2195,7 +2212,6 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
end
consistent = !ismutabletype(t) ? ALWAYS_TRUE : CONSISTENT_IF_NOTRETURNED
effects = Effects(EFFECTS_TOTAL; consistent, nothrow)
merge_effects!(interp, sv, effects)
elseif ehead === :new_opaque_closure
t = Union{}
effects = Effects() # TODO
Expand Down Expand Up @@ -2223,20 +2239,16 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
elseif ehead === :foreigncall
(;rt, effects) = abstract_eval_foreigncall(interp, e, vtypes, sv, mi)
t = rt
merge_effects!(interp, sv, effects)
elseif ehead === :cfunction
effects = EFFECTS_UNKNOWN
merge_effects!(interp, sv, effects)
t = e.args[1]
isa(t, Type) || (t = Any)
abstract_eval_cfunction(interp, e, vtypes, sv)
elseif ehead === :method
t = (length(e.args) == 1) ? Any : Nothing
effects = EFFECTS_UNKNOWN
merge_effects!(interp, sv, effects)
elseif ehead === :copyast
effects = EFFECTS_UNKNOWN
merge_effects!(interp, sv, effects)
t = abstract_eval_value(interp, e.args[1], vtypes, sv)
if t isa Const && t.val isa Expr
# `copyast` makes copies of Exprs
Expand All @@ -2247,6 +2259,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
elseif ehead === :isdefined
sym = e.args[1]
t = Bool
effects = EFFECTS_TOTAL
if isa(sym, SlotNumber)
vtyp = vtypes[slot_id(sym)]
if vtyp.typ === Bottom
Expand Down Expand Up @@ -2275,9 +2288,9 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
@label always_throw
t = Bottom
effects = EFFECTS_THROWS
merge_effects!(interp, sv, effects)
else
t = abstract_eval_value_expr(interp, e, sv)
t = abstract_eval_value_expr(interp, e, vtypes, sv)
effects = EFFECTS_TOTAL
end
return RTEffects(t, effects)
end
Expand Down Expand Up @@ -2319,6 +2332,11 @@ function abstract_eval_phi(interp::AbstractInterpreter, phi::PhiNode, vtypes::Un
return rt
end

function stmt_taints_inbounds_consistency(sv::InferenceState)
flag = sv.src.ssaflags[sv.currpc]
return sv.src.propagate_inbounds || (flag & IR_FLAG_INBOUNDS) != 0
end

function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState)
if !isa(e, Expr)
if isa(e, PhiNode)
Expand All @@ -2327,6 +2345,18 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e),
return abstract_eval_special_value(interp, e, vtypes, sv)
end
(;rt, effects) = abstract_eval_statement_expr(interp, e, vtypes, sv, nothing)
if !effects.noinbounds
flag = sv.src.ssaflags[sv.currpc]
if !sv.src.propagate_inbounds
# The callee read our inbounds flag, but unless we propagate inbounds,
# we ourselves don't read our parent's inbounds.
effects = Effects(effects; noinbounds=true)
end
if (flag & IR_FLAG_INBOUNDS) != 0
effects = Effects(effects; consistent=ALWAYS_FALSE)
end
end
merge_effects!(interp, sv, effects)
e = e::Expr
@assert !isa(rt, TypeVar) "unhandled TypeVar"
rt = maybe_singleton_const(rt)
Expand Down
20 changes: 11 additions & 9 deletions base/compiler/effects.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ following meanings:
This state corresponds to LLVM's `inaccessiblemem_or_argmemonly` function attribute.
- `nonoverlayed::Bool`: indicates that any methods that may be called within this method
are not defined in an [overlayed method table](@ref OverlayMethodTable).
- `noinbounds::Bool`: indicates this method can't be `:consistent` because of bounds checking.
This effect is currently only set on `InferenceState` construction and used to taint
`:consistent`-cy before caching. We may want to track it with more accuracy in the future.
- `noinbounds::Bool`: If set, indicates that this method does not read the parent's :inbounds
state. In particular, it does not have any reached :boundscheck exprs, not propagates inbounds
to any children that do.
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
Expand Down Expand Up @@ -98,10 +98,10 @@ const EFFECT_FREE_IF_INACCESSIBLEMEMONLY = 0x01 << 1
# :inaccessiblememonly bits
const INACCESSIBLEMEM_OR_ARGMEMONLY = 0x01 << 1

const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, true)
const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, true)
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)
const EFFECTS_UNKNOWN′ = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, false) # unknown really
const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, true, true)
const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, true, true)
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)
const EFFECTS_UNKNOWN′ = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, false, true) # unknown really

function Effects(e::Effects = EFFECTS_UNKNOWN′;
consistent::UInt8 = e.consistent,
Expand Down Expand Up @@ -184,7 +184,8 @@ function encode_effects(e::Effects)
((e.terminates % UInt32) << 6) |
((e.notaskstate % UInt32) << 7) |
((e.inaccessiblememonly % UInt32) << 8) |
((e.nonoverlayed % UInt32) << 10)
((e.nonoverlayed % UInt32) << 10)|
((e.noinbounds % UInt32) << 11)
end

function decode_effects(e::UInt32)
Expand All @@ -195,7 +196,8 @@ function decode_effects(e::UInt32)
_Bool((e >> 6) & 0x01),
_Bool((e >> 7) & 0x01),
UInt8((e >> 8) & 0x03),
_Bool((e >> 10) & 0x01))
_Bool((e >> 10) & 0x01),
_Bool((e >> 11) & 0x01))
end

struct EffectsOverride
Expand Down
21 changes: 1 addition & 20 deletions base/compiler/inferencestate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,7 @@ mutable struct InferenceState

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

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

function any_inbounds(code::Vector{Any})
for i = 1:length(code)
stmt = code[i]
if isexpr(stmt, :inbounds)
return true
end
end
return false
end

was_reached(sv::InferenceState, pc::Int) = sv.ssavaluetypes[pc] !== NOT_FOUND

function compute_trycatch(code::Vector{Any}, ip::BitSet)
Expand Down
4 changes: 3 additions & 1 deletion base/compiler/ssair/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -858,7 +858,7 @@ function show_ir_stmts(io::IO, ir::Union{IRCode, CodeInfo, IncrementalCompact},
return bb_idx
end

function finish_show_ir(io::IO, cfg, config::IRShowConfig)
function finish_show_ir(io::IO, cfg::CFG, config::IRShowConfig)
max_bb_idx_size = length(string(length(cfg.blocks)))
config.line_info_preprinter(io, " "^(max_bb_idx_size + 2), 0)
return nothing
Expand Down Expand Up @@ -1012,6 +1012,8 @@ function Base.show(io::IO, e::Effects)
printstyled(io, effectbits_letter(e, :notaskstate, 's'); color=effectbits_color(e, :notaskstate))
print(io, ',')
printstyled(io, effectbits_letter(e, :inaccessiblememonly, 'm'); color=effectbits_color(e, :inaccessiblememonly))
print(io, ',')
printstyled(io, effectbits_letter(e, :noinbounds, 'i'); color=effectbits_color(e, :noinbounds))
print(io, ')')
e.nonoverlayed || printstyled(io, ''; color=:red)
end
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2039,7 +2039,7 @@ function getfield_effects(argtypes::Vector{Any}, @nospecialize(rt))
# If we cannot independently prove inboundsness, taint consistency.
# The inbounds-ness assertion requires dynamic reachability, while
# :consistent needs to be true for all input values.
# N.B. We do not taint for `--check-bounds=no` here -that happens in
# N.B. We do not taint for `--check-bounds=no` here that happens in
# InferenceState.
if length(argtypes) 2 && getfield_nothrow(argtypes[1], argtypes[2], true)
nothrow = true
Expand Down
2 changes: 0 additions & 2 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,6 @@ const var"@_noinline_meta" = var"@noinline"

# BEGIN 1.9 deprecations

@deprecate splat(x) Splat(x) false

# We'd generally like to avoid direct external access to internal fields
# Core.Compiler.is_inlineable and Core.Compiler.set_inlineable! move towards this direction,
# but we need to keep these around for compat
Expand Down
2 changes: 1 addition & 1 deletion base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -816,7 +816,7 @@ export
atreplinit,
exit,
ntuple,
Splat,
splat,

# I/O and events
close,
Expand Down
4 changes: 4 additions & 0 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,10 @@ currently equivalent to the following `setting`s:
however, that by the `:consistent`-cy requirements, any such annotated call
must consistently throw given the same argument values.
!!! note
An explict `@inbounds` annotation inside the function will also disable
constant propagation and not be overriden by :foldable.
---
## `:total`
Expand Down
6 changes: 3 additions & 3 deletions base/iddict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ end

empty(d::IdDict, ::Type{K}, ::Type{V}) where {K, V} = IdDict{K,V}()

function rehash!(d::IdDict, newsz = length(d.ht))
function rehash!(d::IdDict, newsz = length(d.ht)%UInt)
d.ht = ccall(:jl_idtable_rehash, Vector{Any}, (Any, Csize_t), d.ht, newsz)
d
end
Expand All @@ -89,7 +89,7 @@ function setindex!(d::IdDict{K,V}, @nospecialize(val), @nospecialize(key)) where
val = convert(V, val)
end
if d.ndel >= ((3*length(d.ht))>>2)
rehash!(d, max(length(d.ht)>>1, 32))
rehash!(d, max((length(d.ht)%UInt)>>1, 32))
d.ndel = 0
end
inserted = RefValue{Cint}(0)
Expand Down Expand Up @@ -143,7 +143,7 @@ end
_oidd_nextind(a, i) = reinterpret(Int, ccall(:jl_eqtable_nextind, Csize_t, (Any, Csize_t), a, i))

function iterate(d::IdDict{K,V}, idx=0) where {K, V}
idx = _oidd_nextind(d.ht, idx)
idx = _oidd_nextind(d.ht, idx%UInt)
idx == -1 && return nothing
return (Pair{K, V}(d.ht[idx + 1]::K, d.ht[idx + 2]::V), idx + 2)
end
Expand Down
2 changes: 1 addition & 1 deletion base/iterators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ the `zip` iterator is a tuple of values of its subiterators.
`zip()` with no arguments yields an infinite iterator of empty tuples.
See also: [`enumerate`](@ref), [`Splat`](@ref Base.Splat).
See also: [`enumerate`](@ref), [`Base.splat`](@ref).
# Examples
```jldoctest
Expand Down
Loading

0 comments on commit e25433c

Please sign in to comment.