Skip to content

Commit

Permalink
refactor unreachability analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
aviatesk committed May 10, 2022
1 parent e7209e1 commit 6722fdf
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 143 deletions.
22 changes: 16 additions & 6 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1835,7 +1835,13 @@ function abstract_eval_special_value(interp::AbstractInterpreter, @nospecialize(
elseif isa(e, SSAValue)
return abstract_eval_ssavalue(e, sv)
elseif isa(e, SlotNumber) || isa(e, Argument)
return vtypes[slot_id(e)].typ
sn = slot_id(e)
s = vtypes[sn]
if s.undef === true || # may not be defined
s.typ === Bottom # never analyzed
sv.src.slotflags[sn] |= SLOT_USEDUNDEF | SLOT_STATICUNDEF
end
return s.typ
elseif isa(e, GlobalRef)
return abstract_eval_global(e.mod, e.name, sv)
end
Expand Down Expand Up @@ -2022,11 +2028,15 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e),
sym = e.args[1]
t = Bool
if isa(sym, SlotNumber)
vtyp = vtypes[slot_id(sym)]
sn = slot_id(sym)
vtyp = vtypes[sn]
if vtyp.typ === Bottom
sv.src.slotflags[sn] |= SLOT_USEDUNDEF | SLOT_STATICUNDEF
t = Const(false) # never assigned previously
elseif !vtyp.undef
elseif vtyp.undef === false
t = Const(true) # definitely assigned previously
elseif vtyp.undef === true
sv.src.slotflags[sn] |= SLOT_USEDUNDEF | SLOT_STATICUNDEF
end
elseif isa(sym, Symbol)
if isdefined(sv.mod, sym)
Expand Down Expand Up @@ -2332,9 +2342,9 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
if isa(fname, SlotNumber)
changes = StateUpdate(fname, VarState(Any, false), changes, false)
end
elseif hd === :code_coverage_effect ||
(hd !== :boundscheck && # :boundscheck can be narrowed to Bool
hd !== nothing && is_meta_expr_head(hd))
elseif hd === :code_coverage_effect || (hd !== nothing &&
hd !== :boundscheck && # :boundscheck can be narrowed to Bool
is_meta_expr_head(hd))
# these do not generate code
else
t = abstract_eval_statement(interp, stmt, changes, frame)
Expand Down
6 changes: 3 additions & 3 deletions base/compiler/inferencestate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
# (only used in abstractinterpret, doesn't appear in optimize)
struct VarState
typ
undef::Bool
VarState(@nospecialize(typ), undef::Bool) = new(typ, undef)
undef::Union{Nothing,Bool} # nothing if unanalyzed
VarState(@nospecialize(typ), undef::Union{Nothing,Bool}) = new(typ, undef)
end

"""
Expand Down Expand Up @@ -152,7 +152,7 @@ mutable struct InferenceState
stmt_types[1] = stmt_type1 = VarTable(undef, nslots)
for i in 1:nslots
argtyp = (i > nargs) ? Bottom : argtypes[i]
stmt_type1[i] = VarState(argtyp, i > nargs)
stmt_type1[i] = VarState(argtyp, i > nargs && nothing)
slottypes[i] = argtyp
end

Expand Down
66 changes: 58 additions & 8 deletions base/compiler/optimize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,26 @@ mutable struct OptimizationState
linfo::MethodInstance
src::CodeInfo
ir::Union{Nothing, IRCode}
was_reached::Union{Nothing, BitSet}
stmt_info::Vector{Any}
mod::Module
sptypes::Vector{Any} # static parameters
slottypes::Vector{Any}
inlining::InliningState
function OptimizationState(frame::InferenceState, params::OptimizationParams, interp::AbstractInterpreter)
was_reached = BitSet()
for i = 1:length(frame.stmt_types)
if isa(frame.stmt_types[i], VarTable)
push!(was_reached, i)
end
end
s_edges = frame.stmt_edges[1]::Vector{Any}
inlining = InliningState(params,
EdgeTracker(s_edges, frame.valid_worlds),
WorldView(code_cache(interp), frame.world),
interp)
return new(frame.linfo,
frame.src, nothing, frame.stmt_info, frame.mod,
frame.src, nothing, was_reached, frame.stmt_info, frame.mod,
frame.sptypes, frame.slottypes, inlining)
end
function OptimizationState(linfo::MethodInstance, src::CodeInfo, params::OptimizationParams, interp::AbstractInterpreter)
Expand Down Expand Up @@ -131,11 +138,13 @@ mutable struct OptimizationState
WorldView(code_cache(interp), get_world_counter()),
interp)
return new(linfo,
src, nothing, stmt_info, mod,
src, nothing, nothing, stmt_info, mod,
sptypes_from_meth_instance(linfo), slottypes, inlining)
end
end

was_reached((; was_reached)::OptimizationState, pc::Int) = was_reached === nothing || pc in was_reached

function OptimizationState(linfo::MethodInstance, params::OptimizationParams, interp::AbstractInterpreter)
src = retrieve_code_info(linfo)
src === nothing && return nothing
Expand Down Expand Up @@ -571,6 +580,22 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState)
end
end

# TODO
# # eliminate GotoIfNot if either of branch target is unreachable
# for idx = 1:nexpr
# stmt = body[idx]
# if isa(stmt, GotoIfNot) && widenconst(argextype(stmt.cond, src, sv.sptypes)) === Bool
# # replace live GotoIfNot with:
# # - GotoNode if the fallthrough target is unreachable
# # - no-op if the branch target is unreachable
# if states[idx+1] === nothing
# body[idx] = GotoNode(stmt.dest)
# elseif states[stmt.dest] === nothing
# body[idx] = nothing
# end
# end
# end

# Go through and add an unreachable node after every
# Union{} call. Then reindex labels.
code = copy_exprargs(ci.code)
Expand All @@ -585,6 +610,25 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState)
labelchangemap = coverage ? fill(0, length(code)) : ssachangemap
prevloc = zero(eltype(ci.codelocs))
while idx <= length(code)
stmt = code[idx]
if process_meta!(meta, stmt) || !(is_meta_expr(stmt) || was_reached(sv, oldidx))
if oldidx < length(labelchangemap)
ssachangemap[oldidx] != 0 && (ssachangemap[oldidx+1] = ssachangemap[oldidx])
ssachangemap[oldidx] = -1
if coverage
labelchangemap[oldidx] != 0 && (labelchangemap[oldidx+1] = labelchangemap[oldidx])
labelchangemap[oldidx] = -1
end
end
# TODO: It would be more efficient to do this in bulk
deleteat!(code, idx)
deleteat!(codelocs, idx)
deleteat!(ssavaluetypes, idx)
deleteat!(stmtinfo, idx)
deleteat!(ssaflags, idx)
oldidx += 1
continue
end
codeloc = codelocs[idx]
if coverage && codeloc != prevloc && codeloc != 0
# insert a side-effect instruction before the current instruction in the same basic block
Expand All @@ -600,7 +644,16 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState)
idx += 1
prevloc = codeloc
end
if code[idx] isa Expr && ssavaluetypes[idx] === Union{}
if false # TODO isa(stmt, GotoIfNot)
# replace GotoIfNot with:
# - GotoNode if the fallthrough target is unreachable
# - no-op if the branch target is unreachable
if !was_reached(sv, oldidx + 1)
code[idx] = GotoNode(stmt.dest)
elseif !was_reached(sv, stmt.dest)
code[idx] = nothing
end
elseif stmt isa Expr && ssavaluetypes[idx] === Union{}
if !(idx < length(code) && isa(code[idx + 1], ReturnNode) && !isdefined((code[idx + 1]::ReturnNode), :val))
# insert unreachable in the same basic block after the current instruction (splitting it)
insert!(code, idx + 1, ReturnNode())
Expand All @@ -621,9 +674,6 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState)

renumber_ir_elements!(code, ssachangemap, labelchangemap)

for i = 1:length(code)
code[i] = process_meta!(meta, code[i])
end
strip_trailing_junk!(ci, code, stmtinfo)
types = Any[]
stmts = InstructionStream(code, types, stmtinfo, codelocs, ssaflags)
Expand All @@ -634,9 +684,9 @@ end
function process_meta!(meta::Vector{Expr}, @nospecialize stmt)
if isexpr(stmt, :meta) && length(stmt.args) 1
push!(meta, stmt)
return nothing
return true
end
return stmt
return false
end

function slot2reg(ir::IRCode, ci::CodeInfo, sv::OptimizationState)
Expand Down
Loading

0 comments on commit 6722fdf

Please sign in to comment.