diff --git a/Compiler/src/ssair/passes.jl b/Compiler/src/ssair/passes.jl index 0a739815f4ac9..66c774c5da399 100644 --- a/Compiler/src/ssair/passes.jl +++ b/Compiler/src/ssair/passes.jl @@ -361,20 +361,25 @@ function already_inserted(compact::IncrementalCompact, old::OldSSAValue) already_inserted_ssa(compact, compact.idx-1)(0, old) end -function already_inserted_ssa(compact::IncrementalCompact, processed_idx::Int) - return function did_already_insert(phi_arg::Int, old::OldSSAValue) - id = old.id - if id <= length(compact.ir.stmts) - return id <= processed_idx - end - id -= length(compact.ir.stmts) - if id <= length(compact.ir.new_nodes) - return did_already_insert(phi_arg, OldSSAValue(compact.ir.new_nodes.info[id].pos)) - end - id -= length(compact.ir.new_nodes) - @assert id <= length(compact.pending_nodes) - return !(id in compact.pending_perm) +function _already_inserted_ssa(compact::IncrementalCompact, processed_idx::Int, + phi_arg::Int, old::OldSSAValue) + id = old.id + if id <= length(compact.ir.stmts) + return id <= processed_idx + end + id -= length(compact.ir.stmts) + if id <= length(compact.ir.new_nodes) + return _already_inserted_ssa(compact, processed_idx, phi_arg, + OldSSAValue(compact.ir.new_nodes.info[id].pos)) end + id -= length(compact.ir.new_nodes) + @assert id <= length(compact.pending_nodes) + return !(id in compact.pending_perm) +end + +function already_inserted_ssa(compact::IncrementalCompact, processed_idx::Int) + return (phi_arg::Int, old::OldSSAValue) -> + _already_inserted_ssa(compact, processed_idx, phi_arg, old) end function is_pending(compact::IncrementalCompact, old::OldSSAValue) @@ -1647,6 +1652,25 @@ function reachable_blocks(cfg::CFG, from_bb::Int, to_bb::Int) return visited end +function _update_finalizer_insert!(ir::IRCode, lazypostdomtree::LazyPostDomtree, + finalizer_idx::Int, insert_bb::Int, + insert_idx::Union{Int,Nothing}, x::Union{Int,SSAUse}) + defuse_idx = x isa SSAUse ? x.idx : x + defuse_idx == finalizer_idx && return insert_bb, insert_idx + defuse_bb = block_for_inst(ir, defuse_idx) + new_insert_bb = nearest_common_dominator(get!(lazypostdomtree), + insert_bb, defuse_bb) + if new_insert_bb == insert_bb && insert_idx !== nothing + insert_idx = max(insert_idx::Int, defuse_idx) + elseif new_insert_bb == defuse_bb + insert_idx = defuse_idx + else + insert_idx = nothing + end + insert_bb = new_insert_bb + return insert_bb, insert_idx +end + function try_resolve_finalizer!(ir::IRCode, alloc_idx::Int, finalizer_idx::Int, defuse::SSADefUse, inlining::InliningState, lazydomtree::LazyDomtree, lazypostdomtree::LazyPostDomtree, @nospecialize(info::CallInfo)) @@ -1668,24 +1692,12 @@ function try_resolve_finalizer!(ir::IRCode, alloc_idx::Int, finalizer_idx::Int, # Check #2: The insertion block for the finalizer is the post-dominator of all uses insert_bb::Int = finalizer_bb insert_idx::Union{Int,Nothing} = finalizer_idx - function note_defuse!(x::Union{Int,SSAUse}) - defuse_idx = x isa SSAUse ? x.idx : x - defuse_idx == finalizer_idx && return nothing - defuse_bb = block_for_inst(ir, defuse_idx) - new_insert_bb = nearest_common_dominator(get!(lazypostdomtree), - insert_bb, defuse_bb) - if new_insert_bb == insert_bb && insert_idx !== nothing - insert_idx = max(insert_idx::Int, defuse_idx) - elseif new_insert_bb == defuse_bb - insert_idx = defuse_idx - else - insert_idx = nothing - end - insert_bb = new_insert_bb - nothing + for x in defuse.uses + insert_bb, insert_idx = _update_finalizer_insert!(ir, lazypostdomtree, finalizer_idx, insert_bb, insert_idx, x) + end + for x in defuse.defs + insert_bb, insert_idx = _update_finalizer_insert!(ir, lazypostdomtree, finalizer_idx, insert_bb, insert_idx, x) end - foreach(note_defuse!, defuse.uses) - foreach(note_defuse!, defuse.defs) insert_bb != 0 || return nothing # verify post-dominator of all uses exists if !OptimizationParams(inlining.interp).assume_fatal_throw diff --git a/Compiler/src/ssair/show.jl b/Compiler/src/ssair/show.jl index 2947a381be959..f00d0b894546e 100644 --- a/Compiler/src/ssair/show.jl +++ b/Compiler/src/ssair/show.jl @@ -534,37 +534,33 @@ function DILineInfoPrinter(debuginfo, def, showtypes::Bool=false) started::Bool = false if !update_line_only && showtypes && !isa(frame.method, Symbol) && nctx != 1 print(io, linestart) - with_output_color(linecolor, io) do io - print(io, indent("│")) - print(io, "┌ invoke ", frame.method) - println(io) - end + printstyled(io, indent("│"), color=linecolor) + printstyled(io, "┌ invoke ", frame.method, color=linecolor) + println(io) started = true end print(io, linestart) - with_output_color(linecolor, io) do io - print(io, indent("│")) - push!(context, frame) - if update_line_only - update_line_only = false - else - context_depth[] += 1 - nctx != 1 && print(io, started ? "│" : "┌") - end - print(io, " @ ", frame.file) - if frame.line != typemax(frame.line) && frame.line != 0 - print(io, ":", frame.line) - end - print(io, " within `", method_name(frame), "`") - if collapse - method = method_name(frame) - while nctx < nframes - frame = DI[nframes - nctx] - method_name(frame) === method || break - nctx += 1 - push!(context, frame) - print(io, " @ ", frame.file, ":", frame.line) - end + printstyled(io, indent("│"), color=linecolor) + push!(context, frame) + if update_line_only + update_line_only = false + else + context_depth[] += 1 + nctx != 1 && printstyled(io, started ? "│" : "┌", color=linecolor) + end + printstyled(io, " @ ", frame.file, color=linecolor) + if frame.line != typemax(frame.line) && frame.line != 0 + printstyled(io, ":", frame.line, color=linecolor) + end + printstyled(io, " within `", method_name(frame), "`", color=linecolor) + if collapse + method = method_name(frame) + while nctx < nframes + frame = DI[nframes - nctx] + method_name(frame) === method || break + nctx += 1 + push!(context, frame) + printstyled(io, " @ ", frame.file, ":", frame.line, color=linecolor) end end println(io) @@ -681,6 +677,60 @@ function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact}, sptypes, used, cfg, bb_idx; pop_new_node!, only_after, config.bb_color, config.label_dynamic_calls) end +function _print_ir_indentation(io::IO, cfg::CFG, bb_idx::Int, max_bb_idx_size::Int, bb_color, + line_info_preprinter, idx::Int, i::Int; final::Bool=true) + # Compute BB guard rail + if bb_idx > length(cfg.blocks) + # If invariants are violated, print a special leader + linestart = " "^(max_bb_idx_size + 2) # not inside a basic block bracket + inlining_indent = line_info_preprinter(io, linestart, i == 1 ? idx : 0) + printstyled(io, "!!! ", "─"^max_bb_idx_size, color=bb_color) + else + bbrange = cfg.blocks[bb_idx].stmts + # Print line info update + linestart = idx == first(bbrange) ? " " : sprint(io -> printstyled(io, "│ ", color=bb_color), context=io) + linestart *= " "^max_bb_idx_size + # idx == 0 means only indentation is printed, so we don't print linfos + # multiple times if the are new nodes + inlining_indent = line_info_preprinter(io, linestart, i == 1 ? idx : 0) + + if i == 1 && idx == first(bbrange) + bb_idx_str = string(bb_idx) + bb_pad = max_bb_idx_size - length(bb_idx_str) + bb_type = length(cfg.blocks[bb_idx].preds) <= 1 ? "─" : "┄" + printstyled(io, bb_idx_str, " ", bb_type, "─"^bb_pad, color=bb_color) + elseif final && idx == last(bbrange) # print separator + printstyled(io, "└", "─"^(1 + max_bb_idx_size), color=bb_color) + else + printstyled(io, "│ ", " "^max_bb_idx_size, color=bb_color) + end + end + print(io, inlining_indent, " ") + return nothing +end + +function _print_ir_new_node(io::IO, node, code, sptypes::Vector{VarState}, used::BitSet, maxlength_idx::Int, + label_dynamic_calls::Bool, line_info_postprinter, cfg::CFG, bb_idx::Int, + max_bb_idx_size::Int, bb_color, line_info_preprinter, idx::Int, i::Int; final::Bool=true) + _print_ir_indentation(io, cfg, bb_idx, max_bb_idx_size, bb_color, line_info_preprinter, idx, i; final) + + node_idx, new_node_inst, new_node_type = node + @assert new_node_inst !== UNDEF # we filtered these out earlier + show_type = should_print_ssa_type(new_node_inst) + with_output_color(:green, io) do io′ + print_stmt(io′, node_idx, new_node_inst, code, sptypes, used, maxlength_idx, false, show_type, label_dynamic_calls) + end + + if new_node_type === UNDEF + # Try to be robust against errors + printstyled(io, "::#UNDEF", color=:red) + else + line_info_postprinter(io; type = new_node_type, used = node_idx in used, show_type, idx = node_idx) + end + println(io) + return nothing +end + function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact}, idx::Int, line_info_preprinter, line_info_postprinter, sptypes::Vector{VarState}, used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing), only_after::Bool=false, bb_color=:light_black, label_dynamic_calls::Bool=true) @@ -703,58 +753,11 @@ function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact}, end i = 1 - function print_indentation(final::Bool=true) - # Compute BB guard rail - if bb_idx > length(cfg.blocks) - # If invariants are violated, print a special leader - linestart = " "^(max_bb_idx_size + 2) # not inside a basic block bracket - inlining_indent = line_info_preprinter(io, linestart, i == 1 ? idx : 0) - printstyled(io, "!!! ", "─"^max_bb_idx_size, color=bb_color) - else - bbrange = cfg.blocks[bb_idx].stmts - # Print line info update - linestart = idx == first(bbrange) ? " " : sprint(io -> printstyled(io, "│ ", color=bb_color), context=io) - linestart *= " "^max_bb_idx_size - # idx == 0 means only indentation is printed, so we don't print linfos - # multiple times if the are new nodes - inlining_indent = line_info_preprinter(io, linestart, i == 1 ? idx : 0) - - if i == 1 && idx == first(bbrange) - bb_idx_str = string(bb_idx) - bb_pad = max_bb_idx_size - length(bb_idx_str) - bb_type = length(cfg.blocks[bb_idx].preds) <= 1 ? "─" : "┄" - printstyled(io, bb_idx_str, " ", bb_type, "─"^bb_pad, color=bb_color) - elseif final && idx == last(bbrange) # print separator - printstyled(io, "└", "─"^(1 + max_bb_idx_size), color=bb_color) - else - printstyled(io, "│ ", " "^max_bb_idx_size, color=bb_color) - end - end - print(io, inlining_indent, " ") - end - # first, print new nodes that are to be inserted before the current statement - function print_new_node(node; final::Bool=true) - print_indentation(final) - - node_idx, new_node_inst, new_node_type = node - @assert new_node_inst !== UNDEF # we filtered these out earlier - show_type = should_print_ssa_type(new_node_inst) - let maxlength_idx=maxlength_idx, show_type=show_type - with_output_color(:green, io) do io′ - print_stmt(io′, node_idx, new_node_inst, code, sptypes, used, maxlength_idx, false, show_type, label_dynamic_calls) - end - end - - if new_node_type === UNDEF # try to be robust against errors - printstyled(io, "::#UNDEF", color=:red) - else - line_info_postprinter(io; type = new_node_type, used = node_idx in used, show_type, idx = node_idx) - end - println(io) - end while (next = pop_new_node!(idx)) !== nothing - only_after || print_new_node(next; final=false) + only_after || _print_ir_new_node(io, next, code, sptypes, used, maxlength_idx, label_dynamic_calls, + line_info_postprinter, cfg, bb_idx, max_bb_idx_size, bb_color, + line_info_preprinter, idx, i; final=false) i += 1 end @@ -766,7 +769,7 @@ function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact}, # FIXME: `only_after` is hack so that we can call this function to print uncompacted # attach-after nodes when the current node has already been compated already if !only_after - print_indentation(next===nothing) + _print_ir_indentation(io, cfg, bb_idx, max_bb_idx_size, bb_color, line_info_preprinter, idx, i; final=next===nothing) if code isa CodeInfo stmt = statement_indices_to_labels(stmt, cfg) end @@ -786,7 +789,9 @@ function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact}, # finally, print new nodes that are to be inserted after the current statement while next !== nothing - print_new_node(next) + _print_ir_new_node(io, next, code, sptypes, used, maxlength_idx, label_dynamic_calls, + line_info_postprinter, cfg, bb_idx, max_bb_idx_size, bb_color, + line_info_preprinter, idx, i) i += 1 next = pop_new_node!(idx; attach_after=true) end diff --git a/Compiler/src/typeinfer.jl b/Compiler/src/typeinfer.jl index 4b96a7442d4e4..2059a293a1812 100644 --- a/Compiler/src/typeinfer.jl +++ b/Compiler/src/typeinfer.jl @@ -1104,6 +1104,39 @@ function codeinst_as_edge(interp::AbstractInterpreter, sv::InferenceState, @nosp return ci end +function _schedule_edge_infer_task!(caller::AbsIntState, frame::InferenceState, result::InferenceResult, + method::Method, edge_ci::Union{Nothing,CodeInstance}, + edgecycle::Bool, edgelimited::Bool) + mresult = Future{MethodCallResult}() + push!(caller.tasks, function get_infer_result(interp, caller) + update_valid_age!(caller, get_inference_world(interp), frame.valid_worlds) + isinferred = is_inferred(frame) + effects = nothing + edge = nothing + call_result = nothing + if isinferred + edge = result.ci + if edge_ci isa CodeInstance && codeinst_edges_sub(edge_ci, edge.min_world, edge.max_world, edge.edges) + edge = edge_ci # override the edge for tracking invalidation + end + result.ci_as_edge = edge # override the edge for tracking purposes + effects = result.ipo_effects # effects are adjusted already within `finish` for ipo_effects + call_result = result + else + effects = adjust_effects(effects_for_cycle(frame.ipo_effects), method) + add_cycle_backedge!(caller, frame) + end + bestguess = frame.bestguess + exc_bestguess = refine_exception_type(frame.exc_bestguess, effects) + # propagate newly inferred source to the inliner, allowing efficient inlining w/o deserialization: + # note that this result is cached globally exclusively, so we can use this local result destructively + mresult[] = MethodCallResult(interp, caller, method, bestguess, exc_bestguess, effects, + edge, edgecycle, edgelimited, call_result) + return true + end) + return mresult +end + # compute (and cache) an inferred AST and return the current best estimate of the result type function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, caller::AbsIntState, edgecycle::Bool, edgelimited::Bool) mi = specialize_method(method, atype, sparams) @@ -1204,35 +1237,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize assign_parentchild!(frame, caller) # the actual inference task for this edge is going to be scheduled within `typeinf_local` via the callstack queue # while splitting off the rest of the work for this caller into a separate workq thunk - let mresult = Future{MethodCallResult}() - push!(caller.tasks, function get_infer_result(interp, caller) - update_valid_age!(caller, get_inference_world(interp), frame.valid_worlds) - local isinferred = is_inferred(frame) - local effects - local edge = nothing - local call_result = nothing - if isinferred - edge = result.ci - if edge_ci isa CodeInstance && codeinst_edges_sub(edge_ci, edge.min_world, edge.max_world, edge.edges) - edge = edge_ci # override the edge for tracking invalidation - end - result.ci_as_edge = edge # override the edge for tracking purposes - effects = result.ipo_effects # effects are adjusted already within `finish` for ipo_effects - call_result = result - else - effects = adjust_effects(effects_for_cycle(frame.ipo_effects), method) - add_cycle_backedge!(caller, frame) - end - local bestguess = frame.bestguess - local exc_bestguess = refine_exception_type(frame.exc_bestguess, effects) - # propagate newly inferred source to the inliner, allowing efficient inlining w/o deserialization: - # note that this result is cached globally exclusively, so we can use this local result destructively - mresult[] = MethodCallResult(interp, caller, method, bestguess, exc_bestguess, effects, - edge, edgecycle, edgelimited, call_result) - return true - end) - return mresult - end + return _schedule_edge_infer_task!(caller, frame, result, method, edge_ci, edgecycle, edgelimited) elseif frame === true # unresolvable cycle add_remark!(interp, caller, "[typeinf_edge] Unresolvable cycle") diff --git a/base/precompilation.jl b/base/precompilation.jl index 5e9bff46d06a8..c8b2805fcd7ad 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -560,6 +560,21 @@ function precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}}=String[]; IOContext{IO}(io), fancyprint, manifest, ignore_loaded) end +function _visit_indirect_deps!(direct_deps::Dict{PkgId, Vector{PkgId}}, visited::Set{PkgId}, + node::PkgId, all_deps::Set{PkgId}) + if node in visited + return + end + push!(visited, node) + for dep in get(Set{PkgId}, direct_deps, node) + if !(dep in all_deps) + push!(all_deps, dep) + _visit_indirect_deps!(direct_deps, visited, dep, all_deps) + end + end + return +end + function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}}, internal_call::Bool, strict::Bool, @@ -705,25 +720,12 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}}, # A package depends on an extension if it (indirectly) depends on all extension triggers function expand_indirect_dependencies(direct_deps) - function visit!(visited, node, all_deps) - if node in visited - return - end - push!(visited, node) - for dep in get(Set{Base.PkgId}, direct_deps, node) - if !(dep in all_deps) - push!(all_deps, dep) - visit!(visited, dep, all_deps) - end - end - end - local indirect_deps = Dict{Base.PkgId, Set{Base.PkgId}}() for package in keys(direct_deps) # Initialize a set to keep track of all dependencies for 'package' all_deps = Set{Base.PkgId}() visited = Set{Base.PkgId}() - visit!(visited, package, all_deps) + _visit_indirect_deps!(direct_deps, visited, package, all_deps) # Update direct_deps with the complete set of dependencies for 'package' indirect_deps[package] = all_deps end diff --git a/base/weakkeydict.jl b/base/weakkeydict.jl index 1283dc9cbc8cb..e04f8cdd4a1f6 100644 --- a/base/weakkeydict.jl +++ b/base/weakkeydict.jl @@ -195,7 +195,7 @@ function length(t::WeakKeyDict) end function iterate(t::WeakKeyDict{K,V}, state...) where {K, V} - return lock(t) do + @lock t begin while true y = iterate(t.ht, state...) y === nothing && return nothing