diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 7ca6a19eaf967..4700e72ccf874 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -836,9 +836,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 @@ -1010,10 +1008,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 diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index ddf739d104809..c00cfd8d0c26a 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -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 diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index 717a4eec102c2..92e130a65cc75 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -144,9 +144,9 @@ 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) @@ -154,10 +154,10 @@ function concrete_eval_invoke(interp::AbstractInterpreter, 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) @@ -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) @@ -183,6 +183,12 @@ function reprocess_instruction!(interp::AbstractInterpreter, if condval isa Bool function update_phi!(from::Int, to::Int) if length(ir.cfg.blocks[to].preds) == 0 + # Kill the entire block + for idx in ir.cfg.blocks[to].stmts + ir.stmts[idx][:inst] = nothing + ir.stmts[idx][:type] = Union{} + ir.stmts[idx][:flag] = IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW + end return end for idx in ir.cfg.blocks[to].stmts @@ -205,6 +211,7 @@ function reprocess_instruction!(interp::AbstractInterpreter, if bb === nothing bb = block_for_inst(ir, idx) end + ir.stmts[idx][:flag] |= IR_FLAG_NOTHROW if condval ir.stmts[idx][:inst] = nothing ir.stmts[idx][:type] = Any @@ -221,20 +228,25 @@ function reprocess_instruction!(interp::AbstractInterpreter, rt = nothing if isa(inst, Expr) head = inst.head - if head === :call || head === :foreigncall || head === :new + if head === :call || head === :foreigncall || head === :new || head === :splatnew (; 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 || @@ -416,7 +428,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) diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index 79bdf817dc866..289cea14dc01d 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -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 diff --git a/base/reflection.jl b/base/reflection.jl index 2b559b73261c6..bca35f4ce6a10 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -786,6 +786,7 @@ julia> Base.fieldindex(Foo, :z, false) """ function fieldindex(T::DataType, name::Symbol, err::Bool=true) @_foldable_meta + @noinline return Int(ccall(:jl_field_index, Cint, (Any, Any, Cint), T, name, err)+1) end @@ -800,6 +801,7 @@ end function argument_datatype(@nospecialize t) @_total_meta + @noinline return ccall(:jl_argument_datatype, Any, (Any,), t)::Union{Nothing,DataType} end diff --git a/base/tuple.jl b/base/tuple.jl index d59c9239217e3..bc31553404b0f 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -581,6 +581,7 @@ any(x::Tuple{Bool, Bool, Bool}) = x[1]|x[2]|x[3] # a version of `in` esp. for NamedTuple, to make it pure, and not compiled for each tuple length function sym_in(x::Symbol, @nospecialize itr::Tuple{Vararg{Symbol}}) + @noinline @_total_meta for y in itr y === x && return true diff --git a/test/broadcast.jl b/test/broadcast.jl index 1893acc8c1149..5afc60b9c5512 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -1104,7 +1104,7 @@ end end arr = rand(1000) @allocated test(arr) - @test (@allocated test(arr)) == 0 + @test (@allocated test(arr)) <= 16 end @testset "Fix type unstable .&& #43470" begin