Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions Compiler/src/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,8 @@ function const_prop_call(interp::AbstractInterpreter,
end
assign_parentchild!(frame, sv)
if !typeinf(interp, frame)
sv.time_caches += frame.time_caches
sv.time_paused += frame.time_paused
add_remark!(interp, sv, "[constprop] Fresh constant inference hit a cycle")
@assert frame.frameid != 0 && frame.cycleid == frame.frameid
callstack = frame.callstack::Vector{AbsIntState}
Expand Down Expand Up @@ -4357,6 +4359,7 @@ end
# make as much progress on `frame` as possible (by handling cycles)
warnlength::Int = 2500
function typeinf(interp::AbstractInterpreter, frame::InferenceState)
time_before = _time_ns()
callstack = frame.callstack::Vector{AbsIntState}
nextstates = CurrentState[]
takenext = frame.frameid
Expand Down Expand Up @@ -4388,24 +4391,30 @@ function typeinf(interp::AbstractInterpreter, frame::InferenceState)
# get_compileable_sig), but still must be finished up since it may see and
# change the local variables of the InferenceState at currpc, we do this
# even if the nextresult status is already completed.
continue
elseif isdefined(nextstates[nextstateid], :result) || !isempty(callee.ip)
# Next make progress on this frame
prev = length(callee.tasks) + 1
nextstates[nextstateid] = typeinf_local(interp, callee, nextstates[nextstateid])
reverse!(callee.tasks, prev)
elseif callee.cycleid == length(callstack)
# With no active ip's and no cycles, frame is done
finish_nocycle(interp, callee)
time_now = _time_ns()
callee.time_self_ns += (time_now - time_before)
time_before = time_now
finish_nocycle(interp, callee, time_before)
callee.frameid == 0 && break
takenext = length(callstack)
nextstateid = takenext + 1 - frame.frameid
#@assert length(nextstates) == nextstateid + 1
#@assert all(i -> !isdefined(nextstates[i], :result), nextstateid+1:length(nextstates))
resize!(nextstates, nextstateid)
continue
elseif callee.cycleid == callee.frameid
# If the current frame is the top part of a cycle, check if the whole cycle
# is done, and if not, pick the next item to work on.
time_now = _time_ns()
callee.time_self_ns += (time_now - time_before)
time_before = time_now
no_active_ips_in_cycle = true
for i = callee.cycleid:length(callstack)
caller = callstack[i]::InferenceState
Expand All @@ -4416,7 +4425,7 @@ function typeinf(interp::AbstractInterpreter, frame::InferenceState)
end
end
if no_active_ips_in_cycle
finish_cycle(interp, callstack, callee.cycleid)
finish_cycle(interp, callstack, callee.cycleid, time_before)
end
takenext = length(callstack)
nextstateid = takenext + 1 - frame.frameid
Expand All @@ -4426,10 +4435,14 @@ function typeinf(interp::AbstractInterpreter, frame::InferenceState)
else
#@assert length(nextstates) == nextstateid
end
continue
else
# Continue to the next frame in this cycle
takenext = takenext - 1
end
time_now = _time_ns()
callee.time_self_ns += (time_now - time_before)
time_before = time_now
end
#@assert all(nextresult -> !isdefined(nextresult, :result), nextstates)
return is_inferred(frame)
Expand Down
10 changes: 9 additions & 1 deletion Compiler/src/inferencestate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@ mutable struct InferenceState
bestguess #::Type
exc_bestguess
ipo_effects::Effects
time_start::UInt64
time_caches::Float64
time_paused::UInt64
time_self_ns::UInt64

#= flags =#
# Whether to restrict inference of abstract call sites to avoid excessive work
Expand Down Expand Up @@ -392,6 +396,7 @@ mutable struct InferenceState
currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, bb_saw_latestworld, ssavaluetypes, ssaflags, edges, stmt_info,
tasks, pclimitations, limitations, cycle_backedges, callstack, parentid, frameid, cycleid,
result, unreachable, bestguess, exc_bestguess, ipo_effects,
_time_ns(), 0.0, 0, 0,
restrict_abstract_call_sites, cache_mode, insert_coverage,
interp)

Expand Down Expand Up @@ -815,6 +820,8 @@ mutable struct IRInterpretationState
const mi::MethodInstance
world::WorldWithRange
curridx::Int
time_caches::Float64
time_paused::UInt64
const argtypes_refined::Vector{Bool}
const sptypes::Vector{VarState}
const tpdum::TwoPhaseDefUseMap
Expand Down Expand Up @@ -849,7 +856,8 @@ mutable struct IRInterpretationState
tasks = WorkThunk[]
edges = Any[]
callstack = AbsIntState[]
return new(spec_info, ir, mi, WorldWithRange(world, valid_worlds), curridx, argtypes_refined, ir.sptypes, tpdum,
return new(spec_info, ir, mi, WorldWithRange(world, valid_worlds),
curridx, 0.0, 0, argtypes_refined, ir.sptypes, tpdum,
ssa_refined, lazyreachability, tasks, edges, callstack, 0, 0)
end
end
Expand Down
54 changes: 40 additions & 14 deletions Compiler/src/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module Timings

using ..Core
using ..Compiler: -, +, :, Vector, length, first, empty!, push!, pop!, @inline,
@inbounds, copy, backtrace
@inbounds, copy, backtrace, _time_ns

# What we record for any given frame we infer during type inference.
struct InferenceFrameInfo
Expand Down Expand Up @@ -53,8 +53,6 @@ end
Timing(mi_info, start_time, cur_start_time, time, children) = Timing(mi_info, start_time, cur_start_time, time, children, nothing)
Timing(mi_info, start_time) = Timing(mi_info, start_time, start_time, UInt64(0), Timing[])

_time_ns() = ccall(:jl_hrtime, UInt64, ())

# We keep a stack of the Timings for each of the MethodInstances currently being timed.
# Since type inference currently operates via a depth-first search (during abstract
# evaluation), this vector operates like a call stack. The last node in _timings is the
Expand Down Expand Up @@ -103,7 +101,7 @@ function result_edges(interp::AbstractInterpreter, caller::InferenceState)
end
end

function finish!(interp::AbstractInterpreter, caller::InferenceState, validation_world::UInt)
function finish!(interp::AbstractInterpreter, caller::InferenceState, validation_world::UInt, time_before::UInt64)
result = caller.result
#@assert last(result.valid_worlds) <= get_world_counter() || isempty(caller.edges)
if isdefined(result, :ci)
Expand Down Expand Up @@ -142,9 +140,12 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState, validation
if !@isdefined di
di = DebugInfo(result.linfo)
end
ccall(:jl_update_codeinst, Cvoid, (Any, Any, Int32, UInt, UInt, UInt32, Any, Any, Any),
time_now = _time_ns()
time_self_ns = caller.time_self_ns + (time_now - time_before)
time_total = (time_now - caller.time_start - caller.time_paused) * 1e-9
ccall(:jl_update_codeinst, Cvoid, (Any, Any, Int32, UInt, UInt, UInt32, Any, Float64, Float64, Float64, Any, Any),
ci, inferred_result, const_flag, first(result.valid_worlds), last(result.valid_worlds), encode_effects(result.ipo_effects),
result.analysis_results, di, edges)
result.analysis_results, time_total, caller.time_caches, time_self_ns * 1e-9, di, edges)
engine_reject(interp, ci)
codegen = codegen_cache(interp)
if !discard_src && codegen !== nothing && uncompressed isa CodeInfo
Expand Down Expand Up @@ -186,8 +187,8 @@ function finish!(interp::AbstractInterpreter, mi::MethodInstance, ci::CodeInstan
end
ccall(:jl_fill_codeinst, Cvoid, (Any, Any, Any, Any, Int32, UInt, UInt, UInt32, Any, Any, Any),
ci, rettype, exctype, nothing, const_flags, min_world, max_world, ipo_effects, nothing, di, edges)
ccall(:jl_update_codeinst, Cvoid, (Any, Any, Int32, UInt, UInt, UInt32, Any, Any, Any),
ci, nothing, const_flag, min_world, max_world, ipo_effects, nothing, di, edges)
ccall(:jl_update_codeinst, Cvoid, (Any, Any, Int32, UInt, UInt, UInt32, Any, Float64, Float64, Float64, Any, Any),
ci, nothing, const_flag, min_world, max_world, ipo_effects, nothing, 0.0, 0.0, 0.0, di, edges)
code_cache(interp)[mi] = ci
codegen = codegen_cache(interp)
if codegen !== nothing
Expand All @@ -197,14 +198,14 @@ function finish!(interp::AbstractInterpreter, mi::MethodInstance, ci::CodeInstan
return nothing
end

function finish_nocycle(::AbstractInterpreter, frame::InferenceState)
function finish_nocycle(::AbstractInterpreter, frame::InferenceState, time_before::UInt64)
finishinfer!(frame, frame.interp, frame.cycleid)
opt = frame.result.src
if opt isa OptimizationState # implies `may_optimize(caller.interp) === true`
optimize(frame.interp, opt, frame.result)
end
validation_world = get_world_counter()
finish!(frame.interp, frame, validation_world)
finish!(frame.interp, frame, validation_world, time_before)
if isdefined(frame.result, :ci)
# After validation, under the world_counter_lock, set max_world to typemax(UInt) for all dependencies
# (recursively). From that point onward the ordinary backedge mechanism is responsible for maintaining
Expand All @@ -219,7 +220,7 @@ function finish_nocycle(::AbstractInterpreter, frame::InferenceState)
return nothing
end

function finish_cycle(::AbstractInterpreter, frames::Vector{AbsIntState}, cycleid::Int)
function finish_cycle(::AbstractInterpreter, frames::Vector{AbsIntState}, cycleid::Int, time_before::UInt64)
cycle_valid_worlds = WorldRange()
cycle_valid_effects = EFFECTS_TOTAL
for frameid = cycleid:length(frames)
Expand All @@ -236,23 +237,45 @@ function finish_cycle(::AbstractInterpreter, frames::Vector{AbsIntState}, cyclei
caller = frames[frameid]::InferenceState
adjust_cycle_frame!(caller, cycle_valid_worlds, cycle_valid_effects)
finishinfer!(caller, caller.interp, cycleid)
time_now = _time_ns()
caller.time_self_ns += (time_now - time_before)
time_before = time_now
end
time_caches = 0.0 # the total and adjusted time of every entry in the cycle are the same
time_paused = UInt64(0)
for frameid = cycleid:length(frames)
caller = frames[frameid]::InferenceState
opt = caller.result.src
if opt isa OptimizationState # implies `may_optimize(caller.interp) === true`
optimize(caller.interp, opt, caller.result)
time_now = _time_ns()
caller.time_self_ns += (time_now - time_before)
time_before = time_now
end
time_caches += caller.time_caches
time_paused += caller.time_paused
caller.time_paused = UInt64(0)
caller.time_caches = 0.0
end
cycletop = frames[cycleid]::InferenceState
time_start = cycletop.time_start
validation_world = get_world_counter()
cis = CodeInstance[]
for frameid = cycleid:length(frames)
caller = frames[frameid]::InferenceState
finish!(caller.interp, caller, validation_world)
caller.time_start = time_start
caller.time_caches = time_caches
caller.time_paused = time_paused
finish!(caller.interp, caller, validation_world, time_before)
if isdefined(caller.result, :ci)
push!(cis, caller.result.ci)
end
end
if cycletop.parentid != 0
parent = frames[cycletop.parentid]
parent.time_caches += time_caches
parent.time_paused += time_paused
end
# After validation, under the world_counter_lock, set max_world to typemax(UInt) for all dependencies
# (recursively). From that point onward the ordinary backedge mechanism is responsible for maintaining
# validity.
Expand Down Expand Up @@ -792,9 +815,10 @@ function return_cached_result(interp::AbstractInterpreter, method::Method, codei
rt = cached_return_type(codeinst)
exct = codeinst.exctype
effects = ipo_effects(codeinst)
edge = codeinst
update_valid_age!(caller, WorldRange(min_world(codeinst), max_world(codeinst)))
return Future(MethodCallResult(interp, caller, method, rt, exct, effects, edge, edgecycle, edgelimited))
caller.time_caches += reinterpret(Float16, codeinst.time_infer_total)
caller.time_caches += reinterpret(Float16, codeinst.time_infer_cache_saved)
return Future(MethodCallResult(interp, caller, method, rt, exct, effects, codeinst, edgecycle, edgelimited))
end

function MethodCallResult(::AbstractInterpreter, sv::AbsIntState, method::Method,
Expand Down Expand Up @@ -890,7 +914,9 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize
if frame === false
# completely new, but check again after reserving in the engine
if cache_mode == CACHE_MODE_GLOBAL
reserve_start = _time_ns() # subtract engine_reserve (thread-synchronization) time from callers to avoid double-counting
ci_from_engine = engine_reserve(interp, mi)
caller.time_paused += (_time_ns() - reserve_start)
edge_ci = ci_from_engine
codeinst = get(code_cache(interp), mi, nothing)
if codeinst isa CodeInstance # return existing rettype if the code is already inferred
Expand Down
2 changes: 2 additions & 0 deletions Compiler/src/utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -351,3 +351,5 @@ function inbounds_option()
end

is_asserts() = ccall(:jl_is_assertsbuild, Cint, ()) == 1

_time_ns() = ccall(:jl_hrtime, UInt64, ())
2 changes: 0 additions & 2 deletions base/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ include("views.jl")

# numeric operations
include("hashing.jl")
include("rounding.jl")
include("div.jl")
include("float.jl")
include("twiceprecision.jl")
include("complex.jl")
include("rational.jl")
Expand Down
2 changes: 2 additions & 0 deletions base/Base_compiler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ include("operators.jl")
include("pointer.jl")
include("refvalue.jl")
include("cmem.jl")
include("rounding.jl")
include("float.jl")

include("checked.jl")
using .Checked
Expand Down
2 changes: 1 addition & 1 deletion base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ macro _nothrow_meta()
#=:consistent_overlay=#false,
#=:nortcall=#false))
end
# can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping)
# can be used in place of `@assume_effects :noub` (supposed to be used for bootstrapping)
macro _noub_meta()
return _is_internal(__module__) && Expr(:meta, Expr(:purity,
#=:consistent=#false,
Expand Down
Loading