diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 99c291ff8ec4b..21481668d1dd4 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -12,15 +12,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), sv::AbsIntState, max_methods::Int) ๐•ƒโ‚š, ๐•ƒแตข = ipo_lattice(interp), typeinf_lattice(interp) โŠ‘โ‚š = โŠ‘(๐•ƒโ‚š) - if !should_infer_this_call(interp, sv) - add_remark!(interp, sv, "Skipped call in throw block") - # At this point we are guaranteed to end up throwing on this path, - # which is all that's required for :consistent-cy. Of course, we don't - # know anything else about this statement. - effects = Effects(; consistent=ALWAYS_TRUE) - return CallMeta(Any, Any, effects, NoCallInfo()) - end - argtypes = arginfo.argtypes matches = find_matching_methods(๐•ƒแตข, argtypes, atype, method_table(interp), InferenceParams(interp).max_union_splitting, max_methods) diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 2add6c631713f..a237037ace9ef 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -276,7 +276,7 @@ mutable struct InferenceState currbb = currpc = 1 ip = BitSet(1) # TODO BitSetBoundedMinPrioritySet(1) - handler_at, handlers = compute_trycatch(code, BitSet()) + handler_at, handlers = compute_trycatch(code) nssavalues = src.ssavaluetypes::Int ssavalue_uses = find_ssavalue_uses(code, nssavalues) nstmts = length(code) @@ -321,7 +321,6 @@ mutable struct InferenceState restrict_abstract_call_sites = isa(def, Module) # some more setups - InferenceParams(interp).unoptimize_throw_blocks && mark_throw_blocks!(src, handler_at) !iszero(cache_mode & CACHE_MODE_LOCAL) && push!(get_inference_cache(interp), result) return new( @@ -346,7 +345,7 @@ is_inferred(result::InferenceResult) = result.result !== nothing was_reached(sv::InferenceState, pc::Int) = sv.ssavaluetypes[pc] !== NOT_FOUND -function compute_trycatch(code::Vector{Any}, ip::BitSet) +function compute_trycatch(code::Vector{Any}) # The goal initially is to record the frame like this for the state at exit: # 1: (enter 3) # == 0 # 3: (expr) # == 1 @@ -355,7 +354,7 @@ function compute_trycatch(code::Vector{Any}, ip::BitSet) # then we can find all `try`s by walking backwards from :enter statements, # and all `catch`es by looking at the statement after the :enter n = length(code) - empty!(ip) + ip = BitSet() ip.offset = 0 # for _bits_findnext push!(ip, n + 1) handler_at = fill((0, 0), n) @@ -957,30 +956,6 @@ bail_out_apply(::AbstractInterpreter, state::InferenceLoopState, ::InferenceStat bail_out_apply(::AbstractInterpreter, state::InferenceLoopState, ::IRInterpretationState) = state.rt === Any -function should_infer_this_call(interp::AbstractInterpreter, sv::InferenceState) - if InferenceParams(interp).unoptimize_throw_blocks - # Disable inference of calls in throw blocks, since we're unlikely to - # need their types. There is one exception however: If up until now, the - # function has not seen any side effects, we would like to make sure there - # aren't any in the throw block either to enable other optimizations. - if is_stmt_throw_block(get_curr_ssaflag(sv)) - should_infer_for_effects(sv) || return false - end - end - return true -end -function should_infer_for_effects(sv::InferenceState) - def = sv.linfo.def - def isa Method || return false # toplevel frame will not be [semi-]concrete-evaluated - effects = sv.ipo_effects - override = decode_effects_override(def.purity) - effects.consistent === ALWAYS_FALSE && !is_effect_overridden(override, :consistent) && return false - effects.effect_free === ALWAYS_FALSE && !is_effect_overridden(override, :effect_free) && return false - !effects.terminates && !is_effect_overridden(override, :terminates_globally) && return false - return true -end -should_infer_this_call(::AbstractInterpreter, ::IRInterpretationState) = true - add_remark!(::AbstractInterpreter, ::InferenceState, remark) = return add_remark!(::AbstractInterpreter, ::IRInterpretationState, remark) = return diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index d170a062be499..94779b2c6e858 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -23,27 +23,26 @@ const IR_FLAG_INBOUNDS = one(UInt32) << 0 const IR_FLAG_INLINE = one(UInt32) << 1 # This statement is marked as @noinline by user const IR_FLAG_NOINLINE = one(UInt32) << 2 -const IR_FLAG_THROW_BLOCK = one(UInt32) << 3 # This statement was proven :effect_free -const IR_FLAG_EFFECT_FREE = one(UInt32) << 4 +const IR_FLAG_EFFECT_FREE = one(UInt32) << 3 # This statement was proven not to throw -const IR_FLAG_NOTHROW = one(UInt32) << 5 +const IR_FLAG_NOTHROW = one(UInt32) << 4 # This is :consistent -const IR_FLAG_CONSISTENT = one(UInt32) << 6 +const IR_FLAG_CONSISTENT = one(UInt32) << 5 # An optimization pass has updated this statement in a way that may # have exposed information that inference did not see. Re-running # inference on this statement may be profitable. -const IR_FLAG_REFINED = one(UInt32) << 7 +const IR_FLAG_REFINED = one(UInt32) << 6 # This is :noub == ALWAYS_TRUE -const IR_FLAG_NOUB = one(UInt32) << 8 +const IR_FLAG_NOUB = one(UInt32) << 7 # TODO: Both of these should eventually go away once # This is :effect_free == EFFECT_FREE_IF_INACCESSIBLEMEMONLY -const IR_FLAG_EFIIMO = one(UInt32) << 9 +const IR_FLAG_EFIIMO = one(UInt32) << 8 # This is :inaccessiblememonly == INACCESSIBLEMEM_OR_ARGMEMONLY -const IR_FLAG_INACCESSIBLE_OR_ARGMEM = one(UInt32) << 10 +const IR_FLAG_INACCESSIBLE_OR_ARGMEM = one(UInt32) << 9 -const NUM_IR_FLAGS = 11 # sync with julia.h +const NUM_IR_FLAGS = 10 # sync with julia.h const IR_FLAGS_EFFECTS = IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW | IR_FLAG_CONSISTENT | IR_FLAG_NOUB @@ -222,9 +221,8 @@ end _topmod(sv::OptimizationState) = _topmod(sv.mod) -is_stmt_inline(stmt_flag::UInt32) = has_flag(stmt_flag, IR_FLAG_INLINE) -is_stmt_noinline(stmt_flag::UInt32) = has_flag(stmt_flag, IR_FLAG_NOINLINE) -is_stmt_throw_block(stmt_flag::UInt32) = has_flag(stmt_flag, IR_FLAG_THROW_BLOCK) +is_stmt_inline(stmt_flag::UInt32) = has_flag(stmt_flag, IR_FLAG_INLINE) +is_stmt_noinline(stmt_flag::UInt32) = has_flag(stmt_flag, IR_FLAG_NOINLINE) function new_expr_effect_flags(๐•ƒโ‚’::AbstractLattice, args::Vector{Any}, src::Union{IRCode,IncrementalCompact}, pattern_match=nothing) Targ = args[1] @@ -1146,7 +1144,7 @@ plus_saturate(x::Int, y::Int) = max(x, y, x+y) isknowntype(@nospecialize T) = (T === Union{}) || isa(T, Const) || isconcretetype(widenconst(T)) function statement_cost(ex::Expr, line::Int, src::Union{CodeInfo, IRCode}, sptypes::Vector{VarState}, - params::OptimizationParams, error_path::Bool = false) + params::OptimizationParams) #=const=# UNKNOWN_CALL_COST = 20 head = ex.head if is_meta_expr_head(head) @@ -1207,10 +1205,10 @@ function statement_cost(ex::Expr, line::Int, src::Union{CodeInfo, IRCode}, sptyp return 0 elseif (f === Core.memoryrefget || f === Core.memoryref_isassigned) && length(ex.args) >= 3 atyp = argextype(ex.args[2], src, sptypes) - return isknowntype(atyp) ? 1 : error_path ? params.inline_error_path_cost : params.inline_nonleaf_penalty + return isknowntype(atyp) ? 1 : params.inline_nonleaf_penalty elseif f === Core.memoryrefset! && length(ex.args) >= 3 atyp = argextype(ex.args[2], src, sptypes) - return isknowntype(atyp) ? 5 : error_path ? params.inline_error_path_cost : params.inline_nonleaf_penalty + return isknowntype(atyp) ? 5 : params.inline_nonleaf_penalty elseif f === typeassert && isconstType(widenconst(argextype(ex.args[3], src, sptypes))) return 1 end @@ -1226,7 +1224,7 @@ function statement_cost(ex::Expr, line::Int, src::Union{CodeInfo, IRCode}, sptyp if extyp === Union{} return 0 end - return error_path ? params.inline_error_path_cost : params.inline_nonleaf_penalty + return params.inline_nonleaf_penalty elseif head === :foreigncall foreigncall = ex.args[1] if foreigncall isa QuoteNode && foreigncall.value === :jl_string_ptr @@ -1249,7 +1247,7 @@ function statement_cost(ex::Expr, line::Int, src::Union{CodeInfo, IRCode}, sptyp end a = ex.args[2] if a isa Expr - cost = plus_saturate(cost, statement_cost(a, -1, src, sptypes, params, error_path)) + cost = plus_saturate(cost, statement_cost(a, -1, src, sptypes, params)) end return cost elseif head === :copyast @@ -1263,8 +1261,7 @@ function statement_or_branch_cost(@nospecialize(stmt), line::Int, src::Union{Cod thiscost = 0 dst(tgt) = isa(src, IRCode) ? first(src.cfg.blocks[tgt].stmts) : tgt if stmt isa Expr - thiscost = statement_cost(stmt, line, src, sptypes, params, - is_stmt_throw_block(isa(src, IRCode) ? src.stmts.flag[line] : src.ssaflags[line]))::Int + thiscost = statement_cost(stmt, line, src, sptypes, params)::Int elseif stmt isa GotoNode # loops are generally always expensive # but assume that forward jumps are already counted for from diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index 7ee07585fae9b..a3ccdbdfef2b0 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -582,7 +582,7 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, sv::OptimizationState, end # Record the correct exception handler for all critical sections - handler_at, handlers = compute_trycatch(code, BitSet()) + handler_at, handlers = compute_trycatch(code) phi_slots = Vector{Int}[Int[] for _ = 1:length(ir.cfg.blocks)] live_slots = Vector{Int}[Int[] for _ = 1:length(ir.cfg.blocks)] diff --git a/base/compiler/types.jl b/base/compiler/types.jl index b98cf09ff7cf1..048378a843e0c 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -157,11 +157,6 @@ Parameters that control abstract interpretation-based type inference operation. information available. [`Base.@constprop :aggressive`](@ref Base.@constprop) can have a more fine-grained control on this configuration with per-method annotation basis. --- -- `inf_params.unoptimize_throw_blocks::Bool = true`\\ - If `true`, skips inferring calls that are in a block that is known to `throw`. - It may improve the compiler latency without sacrificing the runtime performance - in common situations. ---- - `inf_params.assume_bindings_static::Bool = false`\\ If `true`, assumes that no new bindings will be added, i.e. a non-existing binding at inference time can be assumed to always not exist at runtime (and thus e.g. any access to @@ -177,7 +172,6 @@ struct InferenceParams tuple_complexity_limit_depth::Int ipo_constant_propagation::Bool aggressive_constant_propagation::Bool - unoptimize_throw_blocks::Bool assume_bindings_static::Bool ignore_recursion_hardlimit::Bool @@ -189,7 +183,6 @@ struct InferenceParams tuple_complexity_limit_depth::Int, ipo_constant_propagation::Bool, aggressive_constant_propagation::Bool, - unoptimize_throw_blocks::Bool, assume_bindings_static::Bool, ignore_recursion_hardlimit::Bool) return new( @@ -200,7 +193,6 @@ struct InferenceParams tuple_complexity_limit_depth, ipo_constant_propagation, aggressive_constant_propagation, - unoptimize_throw_blocks, assume_bindings_static, ignore_recursion_hardlimit) end @@ -214,7 +206,6 @@ function InferenceParams( #=tuple_complexity_limit_depth::Int=# 3, #=ipo_constant_propagation::Bool=# true, #=aggressive_constant_propagation::Bool=# false, - #=unoptimize_throw_blocks::Bool=# true, #=assume_bindings_static::Bool=# false, #=ignore_recursion_hardlimit::Bool=# false); max_methods::Int = params.max_methods, @@ -224,7 +215,6 @@ function InferenceParams( tuple_complexity_limit_depth::Int = params.tuple_complexity_limit_depth, ipo_constant_propagation::Bool = params.ipo_constant_propagation, aggressive_constant_propagation::Bool = params.aggressive_constant_propagation, - unoptimize_throw_blocks::Bool = params.unoptimize_throw_blocks, assume_bindings_static::Bool = params.assume_bindings_static, ignore_recursion_hardlimit::Bool = params.ignore_recursion_hardlimit) return InferenceParams( @@ -235,7 +225,6 @@ function InferenceParams( tuple_complexity_limit_depth, ipo_constant_propagation, aggressive_constant_propagation, - unoptimize_throw_blocks, assume_bindings_static, ignore_recursion_hardlimit) end @@ -260,10 +249,6 @@ Parameters that control optimizer operation. tuple return types (in hopes of splitting it up). `opt_params.inline_tupleret_bonus` will be added to `opt_params.inline_cost_threshold` when making inlining decision. --- -- `opt_params.inline_error_path_cost::Int = 20`\\ - Specifies the penalty cost for an un-optimized dynamic call in a block that is known to - `throw`. See also [`(inf_params::InferenceParams).unoptimize_throw_blocks`](@ref InferenceParams). ---- - `opt_params.max_tuple_splat::Int = 32`\\ When attempting to inline `Core._apply_iterate`, abort the optimization if the tuple contains more than this many elements. @@ -290,7 +275,6 @@ struct OptimizationParams inline_cost_threshold::Int inline_nonleaf_penalty::Int inline_tupleret_bonus::Int - inline_error_path_cost::Int max_tuple_splat::Int compilesig_invokes::Bool assume_fatal_throw::Bool @@ -301,7 +285,6 @@ struct OptimizationParams inline_cost_threshold::Int, inline_nonleaf_penalty::Int, inline_tupleret_bonus::Int, - inline_error_path_cost::Int, max_tuple_splat::Int, compilesig_invokes::Bool, assume_fatal_throw::Bool, @@ -311,7 +294,6 @@ struct OptimizationParams inline_cost_threshold, inline_nonleaf_penalty, inline_tupleret_bonus, - inline_error_path_cost, max_tuple_splat, compilesig_invokes, assume_fatal_throw, @@ -324,7 +306,6 @@ function OptimizationParams( #=inline_cost_threshold::Int=# 100, #=inline_nonleaf_penalty::Int=# 1000, #=inline_tupleret_bonus::Int=# 250, - #=inline_error_path_cost::Int=# 20, #=max_tuple_splat::Int=# 32, #=compilesig_invokes::Bool=# true, #=assume_fatal_throw::Bool=# false, @@ -333,7 +314,6 @@ function OptimizationParams( inline_cost_threshold::Int = params.inline_cost_threshold, inline_nonleaf_penalty::Int = params.inline_nonleaf_penalty, inline_tupleret_bonus::Int = params.inline_tupleret_bonus, - inline_error_path_cost::Int = params.inline_error_path_cost, max_tuple_splat::Int = params.max_tuple_splat, compilesig_invokes::Bool = params.compilesig_invokes, assume_fatal_throw::Bool = params.assume_fatal_throw, @@ -343,7 +323,6 @@ function OptimizationParams( inline_cost_threshold, inline_nonleaf_penalty, inline_tupleret_bonus, - inline_error_path_cost, max_tuple_splat, compilesig_invokes, assume_fatal_throw, diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 368395e714054..5e868529b5a91 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -462,51 +462,6 @@ function is_throw_call(e::Expr, code::Vector{Any}) return false end -function mark_throw_blocks!(src::CodeInfo, handler_at::Vector{Tuple{Int, Int}}) - for stmt in find_throw_blocks(src.code, handler_at) - src.ssaflags[stmt] |= IR_FLAG_THROW_BLOCK - end - return nothing -end - -function find_throw_blocks(code::Vector{Any}, handler_at::Vector{Tuple{Int, Int}}) - stmts = BitSet() - n = length(code) - for i in n:-1:1 - s = code[i] - if isa(s, Expr) - if s.head === :gotoifnot - if i+1 in stmts && s.args[2]::Int in stmts - push!(stmts, i) - end - elseif s.head === :return - # see `ReturnNode` handling - elseif is_throw_call(s, code) - if handler_at[i][1] == 0 - push!(stmts, i) - end - elseif i+1 in stmts - push!(stmts, i) - end - elseif isa(s, ReturnNode) - # NOTE: it potentially makes sense to treat unreachable nodes - # (where !isdefined(s, :val)) as `throw` points, but that can cause - # worse codegen around the call site (issue #37558) - elseif isa(s, GotoNode) - if s.label in stmts - push!(stmts, i) - end - elseif isa(s, GotoIfNot) - if i+1 in stmts && s.dest in stmts - push!(stmts, i) - end - elseif i+1 in stmts - push!(stmts, i) - end - end - return stmts -end - # using a function to ensure we can infer this @inline function slot_id(s) isa(s, SlotNumber) && return s.id diff --git a/src/julia.h b/src/julia.h index 8c757f538f5f4..5b468fd1178c1 100644 --- a/src/julia.h +++ b/src/julia.h @@ -266,7 +266,7 @@ typedef union __jl_purity_overrides_t { } _jl_purity_overrides_t; #define NUM_EFFECTS_OVERRIDES 9 -#define NUM_IR_FLAGS 11 +#define NUM_IR_FLAGS 10 // This type describes a single function body typedef struct _jl_code_info_t { @@ -278,15 +278,14 @@ typedef struct _jl_code_info_t { // 1 << 0 = inbounds region // 1 << 1 = callsite inline region // 1 << 2 = callsite noinline region - // 1 << 3 = throw block - // 1 << 4 = :effect_free - // 1 << 5 = :nothrow - // 1 << 6 = :consistent - // 1 << 7 = :refined - // 1 << 8 = :noub - // 1 << 9 = :effect_free_if_inaccessiblememonly - // 1 << 10 = :inaccessiblemem_or_argmemonly - // 1 << 11-18 = callsite effects overrides + // 1 << 3 = :effect_free + // 1 << 4 = :nothrow + // 1 << 5 = :consistent + // 1 << 6 = :refined + // 1 << 7 = :noub + // 1 << 8 = :effect_free_if_inaccessiblememonly + // 1 << 9 = :inaccessiblemem_or_argmemonly + // 1 << 10-18 = callsite effects overrides // miscellaneous data: jl_value_t *method_for_inference_limit_heuristics; // optional method used during inference jl_value_t *linetable; // Table of locations [TODO: make this volatile like slotnames] diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 06341361819c1..ba815ea1a5a4c 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -554,8 +554,7 @@ struct REPLInterpreter <: CC.AbstractInterpreter function REPLInterpreter(limit_aggressive_inference::Bool=false; world::UInt = Base.get_world_counter(), inf_params::CC.InferenceParams = CC.InferenceParams(; - aggressive_constant_propagation=true, - unoptimize_throw_blocks=false), + aggressive_constant_propagation=true), opt_params::CC.OptimizationParams = CC.OptimizationParams(), inf_cache::Vector{CC.InferenceResult} = CC.InferenceResult[], code_cache::REPLInterpreterCache = get_code_cache()) diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index 121e7fad55c90..dc9e6a085cd3a 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -121,7 +121,6 @@ end |> only === Nothing @newinterp Issue48097Interp @MethodTable Issue48097MT CC.method_table(interp::Issue48097Interp) = CC.OverlayMethodTable(CC.get_world_counter(interp), Issue48097MT) -CC.InferenceParams(::Issue48097Interp) = CC.InferenceParams(; unoptimize_throw_blocks=false) function CC.concrete_eval_eligible(interp::Issue48097Interp, @nospecialize(f), result::CC.MethodCallResult, arginfo::CC.ArgInfo, sv::CC.AbsIntState) ret = @invoke CC.concrete_eval_eligible(interp::CC.AbstractInterpreter, diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index a797467ff6173..f8389d75ae970 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -4436,7 +4436,7 @@ let x = Tuple{Int,Any}[ #=19=# (0, Expr(:pop_exception, Core.SSAValue(2))) #=20=# (0, Core.ReturnNode(Core.SlotNumber(3))) ] - handler_at, handlers = Core.Compiler.compute_trycatch(last.(x), Core.Compiler.BitSet()) + handler_at, handlers = Core.Compiler.compute_trycatch(last.(x)) @test map(x->x[1] == 0 ? 0 : handlers[x[1]].enter_idx, handler_at) == first.(x) end