Skip to content

Commit

Permalink
Merge branch 'master' into gb/memset
Browse files Browse the repository at this point in the history
  • Loading branch information
gbaraldi authored Oct 4, 2024
2 parents a891be2 + 636a35d commit a3df253
Show file tree
Hide file tree
Showing 126 changed files with 3,849 additions and 1,855 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
.DS_Store
.idea/*
.vscode/*
.zed/*
*.heapsnapshot
.cache
# Buildkite: Ignore the entire .buildkite directory
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ julia-deps: | $(DIRS) $(build_datarootdir)/julia/base $(build_datarootdir)/julia
julia-stdlib: | $(DIRS) julia-deps
@$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/stdlib

julia-base: julia-deps $(build_sysconfdir)/julia/startup.jl $(build_man1dir)/julia.1 $(build_datarootdir)/julia/julia-config.jl
julia-base: julia-deps $(build_sysconfdir)/julia/startup.jl $(build_man1dir)/julia.1 $(build_datarootdir)/julia/julia-config.jl $(build_datarootdir)/julia/juliac.jl $(build_datarootdir)/julia/juliac-buildscript.jl
@$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/base

julia-libccalltest: julia-deps
Expand Down Expand Up @@ -181,7 +181,7 @@ $(build_sysconfdir)/julia/startup.jl: $(JULIAHOME)/etc/startup.jl | $(build_sysc
@echo Creating usr/etc/julia/startup.jl
@cp $< $@

$(build_datarootdir)/julia/julia-config.jl: $(JULIAHOME)/contrib/julia-config.jl | $(build_datarootdir)/julia
$(build_datarootdir)/julia/%: $(JULIAHOME)/contrib/% | $(build_datarootdir)/julia
$(INSTALL_M) $< $(dir $@)

$(build_depsbindir)/stringreplace: $(JULIAHOME)/contrib/stringreplace.c | $(build_depsbindir)
Expand Down Expand Up @@ -410,7 +410,7 @@ endif
$(INSTALL_F) $(JULIAHOME)/contrib/julia.appdata.xml $(DESTDIR)$(datarootdir)/metainfo/
# Install terminal info database
ifneq ($(WITH_TERMINFO),0)
cp -R -L $(build_datarootdir)/terminfo $(DESTDIR)$(datarootdir)
cp -R -L $(build_datarootdir)/julia/terminfo $(DESTDIR)$(datarootdir)/julia/
endif

# Update RPATH entries and JL_SYSTEM_IMAGE_PATH if $(private_libdir_rel) != $(build_private_libdir_rel)
Expand Down
7 changes: 7 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Julia v1.12 Release Notes
New language features
---------------------

- New option `--trim` for building "trimmed" binaries, where code not provably reachable from entry points
is removed. Entry points can be marked using `Base.Experimental.entrypoint` ([#55047]).
- A new keyword argument `usings::Bool` has been added to `names`. By using this, we can now
find all the names available in module `A` by `names(A; all=true, imported=true, usings=true)`. ([#54609])
- the `@atomic(...)` macro family supports now the reference assignment syntax, e.g.
Expand Down Expand Up @@ -35,6 +37,10 @@ Language changes
expression within a given `:toplevel` expression to make use of macros
defined earlier in the same `:toplevel` expression. ([#53515])

- Trivial infinite loops (like `while true; end`) are no longer undefined
behavior. Infinite loops that actually do things (e.g. have side effects
or sleep) were never and are still not undefined behavior. ([#52999])

Compiler/Runtime improvements
-----------------------------

Expand All @@ -57,6 +63,7 @@ variables. ([#53742]).
* New `--trace-compile-timing` option to report how long each method reported by `--trace-compile` took
to compile, in ms. ([#54662])
* `--trace-compile` now prints recompiled methods in yellow or with a trailing comment if color is not supported ([#55763])
* New `--trace-dispatch` option to report methods that are dynamically dispatched ([#55848]).

Multi-threading changes
-----------------------
Expand Down
1 change: 0 additions & 1 deletion base/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,6 @@ end
include("hashing.jl")
include("rounding.jl")
include("div.jl")
include("rawbigints.jl")
include("float.jl")
include("twiceprecision.jl")
include("complex.jl")
Expand Down
7 changes: 2 additions & 5 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1101,11 +1101,8 @@ function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, srcstyle:
end
else
# Dual-iterator implementation
ret = iterate(iterdest)
@inbounds for a in src
idx, state = ret::NTuple{2,Any}
dest[idx] = a
ret = iterate(iterdest, state)
for (Idest, Isrc) in zip(iterdest, itersrc)
@inbounds dest[Idest] = src[Isrc]
end
end
end
Expand Down
4 changes: 4 additions & 0 deletions base/abstractdict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,10 @@ Dict{String, Float64} with 3 entries:
julia> ans == mergewith(+)(a, b)
true
julia> mergewith(-, Dict(), Dict(:a=>1)) # Combining function only used if key is present in both
Dict{Any, Any} with 1 entry:
:a => 1
```
"""
mergewith(combine, d::AbstractDict, others::AbstractDict...) =
Expand Down
1,404 changes: 783 additions & 621 deletions base/compiler/abstractinterpretation.jl

Large diffs are not rendered by default.

127 changes: 111 additions & 16 deletions base/compiler/inferencestate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ mutable struct InferenceState
slottypes::Vector{Any}
src::CodeInfo
cfg::CFG
method_info::MethodInfo
spec_info::SpecInfo

#= intermediate states for local abstract interpretation =#
currbb::Int
Expand All @@ -251,6 +251,7 @@ mutable struct InferenceState
stmt_info::Vector{CallInfo}

#= intermediate states for interprocedural abstract interpretation =#
tasks::Vector{WorkThunk}
pclimitations::IdSet{InferenceState} # causes of precision restrictions (LimitedAccuracy) on currpc ssavalue
limitations::IdSet{InferenceState} # causes of precision restrictions (LimitedAccuracy) on return
cycle_backedges::Vector{Tuple{InferenceState, Int}} # call-graph backedges connecting from callee to caller
Expand Down Expand Up @@ -293,7 +294,7 @@ mutable struct InferenceState
sptypes = sptypes_from_meth_instance(mi)
code = src.code::Vector{Any}
cfg = compute_basic_blocks(code)
method_info = MethodInfo(src)
spec_info = SpecInfo(src)

currbb = currpc = 1
ip = BitSet(1) # TODO BitSetBoundedMinPrioritySet(1)
Expand Down Expand Up @@ -328,6 +329,7 @@ mutable struct InferenceState
limitations = IdSet{InferenceState}()
cycle_backedges = Vector{Tuple{InferenceState,Int}}()
callstack = AbsIntState[]
tasks = WorkThunk[]

valid_worlds = WorldRange(1, get_world_counter())
bestguess = Bottom
Expand All @@ -349,9 +351,9 @@ mutable struct InferenceState
restrict_abstract_call_sites = isa(def, Module)

this = new(
mi, world, mod, sptypes, slottypes, src, cfg, method_info,
mi, world, mod, sptypes, slottypes, src, cfg, spec_info,
currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, ssavaluetypes, stmt_edges, stmt_info,
pclimitations, limitations, cycle_backedges, callstack, 0, 0, 0,
tasks, pclimitations, limitations, cycle_backedges, callstack, 0, 0, 0,
result, unreachable, valid_worlds, bestguess, exc_bestguess, ipo_effects,
restrict_abstract_call_sites, cache_mode, insert_coverage,
interp)
Expand Down Expand Up @@ -789,7 +791,7 @@ end

# TODO add `result::InferenceResult` and put the irinterp result into the inference cache?
mutable struct IRInterpretationState
const method_info::MethodInfo
const spec_info::SpecInfo
const ir::IRCode
const mi::MethodInstance
const world::UInt
Expand All @@ -800,13 +802,14 @@ mutable struct IRInterpretationState
const ssa_refined::BitSet
const lazyreachability::LazyCFGReachability
valid_worlds::WorldRange
const tasks::Vector{WorkThunk}
const edges::Vector{Any}
callstack #::Vector{AbsIntState}
frameid::Int
parentid::Int

function IRInterpretationState(interp::AbstractInterpreter,
method_info::MethodInfo, ir::IRCode, mi::MethodInstance, argtypes::Vector{Any},
spec_info::SpecInfo, ir::IRCode, mi::MethodInstance, argtypes::Vector{Any},
world::UInt, min_world::UInt, max_world::UInt)
curridx = 1
given_argtypes = Vector{Any}(undef, length(argtypes))
Expand All @@ -825,10 +828,11 @@ mutable struct IRInterpretationState
ssa_refined = BitSet()
lazyreachability = LazyCFGReachability(ir)
valid_worlds = WorldRange(min_world, max_world == typemax(UInt) ? get_world_counter() : max_world)
tasks = WorkThunk[]
edges = Any[]
callstack = AbsIntState[]
return new(method_info, ir, mi, world, curridx, argtypes_refined, ir.sptypes, tpdum,
ssa_refined, lazyreachability, valid_worlds, edges, callstack, 0, 0)
return new(spec_info, ir, mi, world, curridx, argtypes_refined, ir.sptypes, tpdum,
ssa_refined, lazyreachability, valid_worlds, tasks, edges, callstack, 0, 0)
end
end

Expand All @@ -841,14 +845,13 @@ function IRInterpretationState(interp::AbstractInterpreter,
else
isa(src, CodeInfo) || return nothing
end
method_info = MethodInfo(src)
spec_info = SpecInfo(src)
ir = inflate_ir(src, mi)
argtypes = va_process_argtypes(optimizer_lattice(interp), argtypes, src.nargs, src.isva)
return IRInterpretationState(interp, method_info, ir, mi, argtypes, world,
return IRInterpretationState(interp, spec_info, ir, mi, argtypes, world,
codeinst.min_world, codeinst.max_world)
end


# AbsIntState
# ===========

Expand All @@ -870,6 +873,7 @@ function print_callstack(frame::AbsIntState)
print(frame_instance(sv))
is_cached(sv) || print(" [uncached]")
sv.parentid == idx - 1 || print(" [parent=", sv.parentid, "]")
isempty(callers_in_cycle(sv)) || print(" [cycle=", sv.cycleid, "]")
println()
@assert sv.frameid == idx
end
Expand Down Expand Up @@ -922,11 +926,11 @@ is_constproped(::IRInterpretationState) = true
is_cached(sv::InferenceState) = !iszero(sv.cache_mode & CACHE_MODE_GLOBAL)
is_cached(::IRInterpretationState) = false

method_info(sv::InferenceState) = sv.method_info
method_info(sv::IRInterpretationState) = sv.method_info
spec_info(sv::InferenceState) = sv.spec_info
spec_info(sv::IRInterpretationState) = sv.spec_info

propagate_inbounds(sv::AbsIntState) = method_info(sv).propagate_inbounds
method_for_inference_limit_heuristics(sv::AbsIntState) = method_info(sv).method_for_inference_limit_heuristics
propagate_inbounds(sv::AbsIntState) = spec_info(sv).propagate_inbounds
method_for_inference_limit_heuristics(sv::AbsIntState) = spec_info(sv).method_for_inference_limit_heuristics

frame_world(sv::InferenceState) = sv.world
frame_world(sv::IRInterpretationState) = sv.world
Expand Down Expand Up @@ -994,7 +998,10 @@ of the same cycle, only if it is part of a cycle with multiple frames.
function callers_in_cycle(sv::InferenceState)
callstack = sv.callstack::Vector{AbsIntState}
cycletop = cycleid = sv.cycleid
while cycletop < length(callstack) && (callstack[cycletop + 1]::InferenceState).cycleid == cycleid
while cycletop < length(callstack)
frame = callstack[cycletop + 1]
frame isa InferenceState || break
frame.cycleid == cycleid || break
cycletop += 1
end
return AbsIntCycle(callstack, cycletop == cycleid ? 0 : cycleid, cycletop)
Expand Down Expand Up @@ -1054,6 +1061,7 @@ function merge_effects!(::AbstractInterpreter, caller::InferenceState, effects::
effects = Effects(effects; effect_free=ALWAYS_TRUE)
end
caller.ipo_effects = merge_effects(caller.ipo_effects, effects)
nothing
end
merge_effects!(::AbstractInterpreter, ::IRInterpretationState, ::Effects) = return

Expand Down Expand Up @@ -1116,3 +1124,90 @@ function get_max_methods_for_module(mod::Module)
max_methods < 0 && return nothing
return max_methods
end

"""
Future{T}
Delayed return value for a value of type `T`, similar to RefValue{T}, but
explicitly represents completed as a `Bool` rather than as `isdefined`.
Set once with `f[] = v` and accessed with `f[]` afterwards.
Can also be constructed with the `completed` flag value and a closure to
produce `x`, as well as the additional arguments to avoid always capturing the
same couple of values.
"""
struct Future{T}
later::Union{Nothing,RefValue{T}}
now::Union{Nothing,T}
Future{T}() where {T} = new{T}(RefValue{T}(), nothing)
Future{T}(x) where {T} = new{T}(nothing, x)
Future(x::T) where {T} = new{T}(nothing, x)
end
isready(f::Future) = f.later === nothing
getindex(f::Future{T}) where {T} = (later = f.later; later === nothing ? f.now::T : later[])
setindex!(f::Future, v) = something(f.later)[] = v
convert(::Type{Future{T}}, x) where {T} = Future{T}(x) # support return type conversion
convert(::Type{Future{T}}, x::Future) where {T} = x::Future{T}
function Future{T}(f, immediate::Bool, interp::AbstractInterpreter, sv::AbsIntState) where {T}
if immediate
return Future{T}(f(interp, sv))
else
@assert applicable(f, interp, sv)
result = Future{T}()
push!(sv.tasks, function (interp, sv)
result[] = f(interp, sv)
return true
end)
return result
end
end
function Future{T}(f, prev::Future{S}, interp::AbstractInterpreter, sv::AbsIntState) where {T, S}
later = prev.later
if later === nothing
return Future{T}(f(prev[], interp, sv))
else
@assert Core._hasmethod(Tuple{Core.Typeof(f), S, typeof(interp), typeof(sv)})
result = Future{T}()
push!(sv.tasks, function (interp, sv)
result[] = f(later[], interp, sv) # capture just later, instead of all of prev
return true
end)
return result
end
end


"""
doworkloop(args...)
Run a tasks inside the abstract interpreter, returning false if there are none.
Tasks will be run in DFS post-order tree order, such that all child tasks will
be run in the order scheduled, prior to running any subsequent tasks. This
allows tasks to generate more child tasks, which will be run before anything else.
Each task will be run repeatedly when returning `false`, until it returns `true`.
"""
function doworkloop(interp::AbstractInterpreter, sv::AbsIntState)
tasks = sv.tasks
prev = length(tasks)
prev == 0 && return false
task = pop!(tasks)
completed = task(interp, sv)
tasks = sv.tasks # allow dropping gc root over the previous call
completed isa Bool || throw(TypeError(:return, "", Bool, task)) # print the task on failure as part of the error message, instead of just "@ workloop:line"
completed || push!(tasks, task)
# efficient post-order visitor: items pushed are executed in reverse post order such
# that later items are executed before earlier ones, but are fully executed
# (including any dependencies scheduled by them) before going on to the next item
reverse!(tasks, #=start=#prev)
return true
end


#macro workthunk(name::Symbol, body)
# name = esc(name)
# body = esc(body)
# return replace_linenums!(
# :(function $name($(esc(interp)), $(esc(sv)))
# $body
# end), __source__)
#end
14 changes: 7 additions & 7 deletions base/compiler/optimize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -644,10 +644,10 @@ function ((; code_cache)::GetNativeEscapeCache)(mi::MethodInstance)
return false
end

function refine_effects!(interp::AbstractInterpreter, sv::PostOptAnalysisState)
function refine_effects!(interp::AbstractInterpreter, opt::OptimizationState, sv::PostOptAnalysisState)
if !is_effect_free(sv.result.ipo_effects) && sv.all_effect_free && !isempty(sv.ea_analysis_pending)
ir = sv.ir
nargs = length(ir.argtypes)
nargs = Int(opt.src.nargs)
estate = EscapeAnalysis.analyze_escapes(ir, nargs, optimizer_lattice(interp), GetNativeEscapeCache(interp))
argescapes = EscapeAnalysis.ArgEscapeCache(estate)
stack_analysis_result!(sv.result, argescapes)
Expand Down Expand Up @@ -939,7 +939,8 @@ function check_inconsistentcy!(sv::PostOptAnalysisState, scanner::BBScanner)
end
end

function ipo_dataflow_analysis!(interp::AbstractInterpreter, ir::IRCode, result::InferenceResult)
function ipo_dataflow_analysis!(interp::AbstractInterpreter, opt::OptimizationState,
ir::IRCode, result::InferenceResult)
if !is_ipo_dataflow_analysis_profitable(result.ipo_effects)
return false
end
Expand Down Expand Up @@ -967,13 +968,13 @@ function ipo_dataflow_analysis!(interp::AbstractInterpreter, ir::IRCode, result:
end
end

return refine_effects!(interp, sv)
return refine_effects!(interp, opt, sv)
end

# run the optimization work
function optimize(interp::AbstractInterpreter, opt::OptimizationState, caller::InferenceResult)
@timeit "optimizer" ir = run_passes_ipo_safe(opt.src, opt, caller)
ipo_dataflow_analysis!(interp, ir, caller)
@timeit "optimizer" ir = run_passes_ipo_safe(opt.src, opt)
ipo_dataflow_analysis!(interp, opt, ir, caller)
return finish(interp, opt, ir, caller)
end

Expand All @@ -995,7 +996,6 @@ matchpass(::Nothing, _, _) = false
function run_passes_ipo_safe(
ci::CodeInfo,
sv::OptimizationState,
caller::InferenceResult,
optimize_until = nothing, # run all passes by default
)
__stage__ = 0 # used by @pass
Expand Down
Loading

0 comments on commit a3df253

Please sign in to comment.