Skip to content

Commit

Permalink
Allow irinterp to refine nothrow effect
Browse files Browse the repository at this point in the history
This addresses a remaining todo in the irinterp code to allow it
to compute whether its particular evaluation refined `nothrow`.
As a result, we can re-enable it for a larger class of ir (we had
previously disabled it to avoid regressing cases where regular
constprop was able to prove a `nothrow` refinement, but irinterp
was not).
  • Loading branch information
Keno committed Dec 31, 2022
1 parent 4d206c9 commit 47c47af
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 16 deletions.
9 changes: 4 additions & 5 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -830,9 +830,7 @@ function concrete_eval_eligible(interp::AbstractInterpreter,
if is_all_const_arg(arginfo, #=start=#2)
return true
else
# TODO: `is_nothrow` is not an actual requirement here, this is just a hack
# to avoid entering semi concrete eval while it doesn't properly override effects
return is_nothrow(result.effects) ? false : nothing
return false
end
end
return nothing
Expand Down Expand Up @@ -1004,10 +1002,11 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter,
ir = codeinst_to_ir(interp, code)
if isa(ir, IRCode)
irsv = IRInterpretationState(interp, ir, mi, sv.world, arginfo.argtypes)
rt = ir_abstract_constant_propagation(interp, irsv)
rt, nothrow = ir_abstract_constant_propagation(interp, irsv)
@assert !(rt isa Conditional || rt isa MustAlias) "invalid lattice element returned from IR interpretation"
if !isa(rt, Type) || typeintersect(rt, Bool) === Union{}
return ConstCallResults(rt, SemiConcreteResult(mi, ir, result.effects), result.effects, mi)
new_effects = Effects(result.effects; nothrow=nothrow)
return ConstCallResults(rt, SemiConcreteResult(mi, ir, new_effects), new_effects, mi)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/optimize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState)
insert!(codelocs, idx + 1, codelocs[idx])
insert!(ssavaluetypes, idx + 1, Union{})
insert!(stmtinfo, idx + 1, NoCallInfo())
insert!(ssaflags, idx + 1, ssaflags[idx])
insert!(ssaflags, idx + 1, IR_FLAG_NOTHROW)
if ssachangemap === nothing
ssachangemap = fill(0, nstmts)
end
Expand Down
31 changes: 22 additions & 9 deletions base/compiler/ssair/irinterp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -144,20 +144,20 @@ function concrete_eval_invoke(interp::AbstractInterpreter,
inst::Expr, mi::MethodInstance, irsv::IRInterpretationState)
mi_cache = WorldView(code_cache(interp), irsv.world)
code = get(mi_cache, mi, nothing)
code === nothing && return nothing
code === nothing && return Pair{Any, Bool}(nothing, false)
argtypes = collect_argtypes(interp, inst.args[2:end], nothing, irsv.ir)
argtypes === nothing && return Union{}
argtypes === nothing && return Pair{Any, Bool}(Union{}, false)
effects = decode_effects(code.ipo_purity_bits)
if is_foldable(effects) && is_all_const_arg(argtypes, #=start=#1)
args = collect_const_args(argtypes, #=start=#1)
world = get_world_counter(interp)
value = try
Core._call_in_world_total(world, args...)
catch
return Union{}
return Pair{Any, Bool}(Union{}, false)
end
if is_inlineable_constant(value)
return Const(value)
return Pair{Any, Bool}(Const(value), true)
end
else
ir′ = codeinst_to_ir(interp, code)
Expand All @@ -166,7 +166,7 @@ function concrete_eval_invoke(interp::AbstractInterpreter,
return _ir_abstract_constant_propagation(interp, irsv′)
end
end
return nothing
return Pair{Any, Bool}(nothing, is_nothrow(effects))
end

function abstract_eval_phi_stmt(interp::AbstractInterpreter, phi::PhiNode, ::Int, irsv::IRInterpretationState)
Expand Down Expand Up @@ -225,16 +225,21 @@ function reprocess_instruction!(interp::AbstractInterpreter,
(; rt, effects) = abstract_eval_statement_expr(interp, inst, nothing, ir, irsv.mi)
# All other effects already guaranteed effect free by construction
if is_nothrow(effects)
ir.stmts[idx][:flag] |= IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW
if isa(rt, Const) && is_inlineable_constant(rt.val)
ir.stmts[idx][:inst] = quoted(rt.val)
else
ir.stmts[idx][:flag] |= IR_FLAG_EFFECT_FREE
end
end
elseif head === :invoke
mi′ = inst.args[1]::MethodInstance
if mi′ !== irsv.mi # prevent infinite loop
rt = concrete_eval_invoke(interp, inst, mi′, irsv)
rt, nothrow = concrete_eval_invoke(interp, inst, mi′, irsv)
if nothrow
ir.stmts[idx][:flag] |= IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW
if isa(rt, Const) && is_inlineable_constant(rt.val)
ir.stmts[idx][:inst] = quoted(rt.val)
end
end
end
elseif head === :throw_undef_if_not || # TODO: Terminate interpretation early if known false?
head === :gc_preserve_begin ||
Expand Down Expand Up @@ -416,7 +421,15 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR
end
end

return maybe_singleton_const(ultimate_rt)
nothrow = true
for i = 1:length(ir.stmts)
if (ir.stmts[i][:flag] & IR_FLAG_NOTHROW) == 0
nothrow = false
break
end
end

return Pair{Any, Bool}(maybe_singleton_const(ultimate_rt), nothrow)
end

function ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IRInterpretationState)
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/ssair/slot2ssa.jl
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ function strip_trailing_junk!(ci::CodeInfo, code::Vector{Any}, info::Vector{Call
push!(ssavaluetypes, Union{})
push!(codelocs, 0)
push!(info, NoCallInfo())
push!(ssaflags, IR_FLAG_NULL)
push!(ssaflags, IR_FLAG_NOTHROW)
end
nothing
end
Expand Down

0 comments on commit 47c47af

Please sign in to comment.