Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

inference: remove throw block deoptimization completely #49260

Merged
merged 7 commits into from
Aug 8, 2024
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
9 changes: 0 additions & 9 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
sv::AbsIntState, max_methods::Int)
𝕃ₚ, 𝕃ᵢ = ipo_lattice(interp), typeinf_lattice(interp)
ₚ, ₚ, = partialorder(𝕃ₚ), join(𝕃ₚ), join(𝕃ᵢ)
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_method_matches(interp, argtypes, atype; max_methods)
if isa(matches, FailedMethodMatch)
Expand Down
3 changes: 1 addition & 2 deletions base/compiler/compiler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,7 @@ baremodule BuildSettings
using Core: ARGS, include
using Core.Compiler: >, getindex, length

MAX_METHODS::Int = 3
UNOPTIMIZE_THROW_BLOCKS::Bool = true
global MAX_METHODS::Int = 3

if length(ARGS) > 2 && ARGS[2] === "--buildsettings"
include(BuildSettings, ARGS[3])
Expand Down
25 changes: 0 additions & 25 deletions base/compiler/inferencestate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,6 @@ mutable struct InferenceState
restrict_abstract_call_sites = isa(def, Module)

# some more setups
InferenceParams(interp).unoptimize_throw_blocks && mark_throw_blocks!(src, handler_info)
!iszero(cache_mode & CACHE_MODE_LOCAL) && push!(get_inference_cache(interp), result)

this = new(
Expand Down Expand Up @@ -1102,30 +1101,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

Expand Down
40 changes: 18 additions & 22 deletions base/compiler/optimize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,29 @@ 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
# This statement is on a code path that eventually `throw`s.
const IR_FLAG_THROW_BLOCK = one(UInt32) << 3
# 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) << 4
const IR_FLAG_REFINED = one(UInt32) << 3
# This statement is proven :consistent
const IR_FLAG_CONSISTENT = one(UInt32) << 5
const IR_FLAG_CONSISTENT = one(UInt32) << 4
# This statement is proven :effect_free
const IR_FLAG_EFFECT_FREE = one(UInt32) << 6
const IR_FLAG_EFFECT_FREE = one(UInt32) << 5
# This statement is proven :nothrow
const IR_FLAG_NOTHROW = one(UInt32) << 7
const IR_FLAG_NOTHROW = one(UInt32) << 6
# This statement is proven :terminates
const IR_FLAG_TERMINATES = one(UInt32) << 8
const IR_FLAG_TERMINATES = one(UInt32) << 7
# This statement is proven :noub
const IR_FLAG_NOUB = one(UInt32) << 9
const IR_FLAG_NOUB = one(UInt32) << 8
# TODO: Both of these should eventually go away once
# This statement is :effect_free == EFFECT_FREE_IF_INACCESSIBLEMEMONLY
const IR_FLAG_EFIIMO = one(UInt32) << 10
const IR_FLAG_EFIIMO = one(UInt32) << 9
# This statement is :inaccessiblememonly == INACCESSIBLEMEM_OR_ARGMEMONLY
const IR_FLAG_INACCESSIBLEMEM_OR_ARGMEM = one(UInt32) << 11
const IR_FLAG_INACCESSIBLEMEM_OR_ARGMEM = one(UInt32) << 10
# This statement has no users and may be deleted if flags get refined to IR_FLAGS_REMOVABLE
const IR_FLAG_UNUSED = one(UInt32) << 12
const IR_FLAG_UNUSED = one(UInt32) << 11

const NUM_IR_FLAGS = 13 # sync with julia.h
const NUM_IR_FLAGS = 12 # sync with julia.h

const IR_FLAGS_EFFECTS =
IR_FLAG_CONSISTENT | IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW | IR_FLAG_TERMINATES | IR_FLAG_NOUB
Expand Down Expand Up @@ -249,9 +247,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]
Expand Down Expand Up @@ -1272,7 +1269,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)
Expand Down Expand Up @@ -1333,10 +1330,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
Expand All @@ -1352,7 +1349,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
Expand All @@ -1375,7 +1372,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
Expand All @@ -1389,8 +1386,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
Expand Down
21 changes: 0 additions & 21 deletions base/compiler/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,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
Expand All @@ -176,7 +171,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

Expand All @@ -188,7 +182,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(
Expand All @@ -199,7 +192,6 @@ struct InferenceParams
tuple_complexity_limit_depth,
ipo_constant_propagation,
aggressive_constant_propagation,
unoptimize_throw_blocks,
assume_bindings_static,
ignore_recursion_hardlimit)
end
Expand All @@ -213,7 +205,6 @@ function InferenceParams(
#=tuple_complexity_limit_depth::Int=# 3,
#=ipo_constant_propagation::Bool=# true,
#=aggressive_constant_propagation::Bool=# false,
#=unoptimize_throw_blocks::Bool=# BuildSettings.UNOPTIMIZE_THROW_BLOCKS,
#=assume_bindings_static::Bool=# false,
#=ignore_recursion_hardlimit::Bool=# false);
max_methods::Int = params.max_methods,
Expand All @@ -223,7 +214,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(
Expand All @@ -234,7 +224,6 @@ function InferenceParams(
tuple_complexity_limit_depth,
ipo_constant_propagation,
aggressive_constant_propagation,
unoptimize_throw_blocks,
assume_bindings_static,
ignore_recursion_hardlimit)
end
Expand All @@ -259,10 +248,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.
Expand All @@ -289,7 +274,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
Expand All @@ -300,7 +284,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,
Expand All @@ -310,7 +293,6 @@ struct OptimizationParams
inline_cost_threshold,
inline_nonleaf_penalty,
inline_tupleret_bonus,
inline_error_path_cost,
max_tuple_splat,
compilesig_invokes,
assume_fatal_throw,
Expand All @@ -323,7 +305,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,
Expand All @@ -332,7 +313,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,
Expand All @@ -342,7 +322,6 @@ function OptimizationParams(
inline_cost_threshold,
inline_nonleaf_penalty,
inline_tupleret_bonus,
inline_error_path_cost,
max_tuple_splat,
compilesig_invokes,
assume_fatal_throw,
Expand Down
21 changes: 10 additions & 11 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ typedef union __jl_purity_overrides_t {
} _jl_purity_overrides_t;

#define NUM_EFFECTS_OVERRIDES 10
#define NUM_IR_FLAGS 13
#define NUM_IR_FLAGS 12

// This type describes a single function body
typedef struct _jl_code_info_t {
Expand All @@ -288,16 +288,15 @@ 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 = refined statement
// 1 << 5 = :consistent
// 1 << 6 = :effect_free
// 1 << 7 = :nothrow
// 1 << 8 = :terminates
// 1 << 9 = :noub
// 1 << 10 = :effect_free_if_inaccessiblememonly
// 1 << 11 = :inaccessiblemem_or_argmemonly
// 1 << 12-19 = callsite effects overrides
// 1 << 3 = refined statement
// 1 << 4 = :consistent
// 1 << 5 = :effect_free
// 1 << 6 = :nothrow
// 1 << 7 = :terminates
// 1 << 8 = :noub
// 1 << 9 = :effect_free_if_inaccessiblememonly
// 1 << 10 = :inaccessiblemem_or_argmemonly
// 1 << 11-19 = callsite effects overrides
// miscellaneous data:
jl_array_t *slotnames; // names of local variables
jl_array_t *slotflags; // local var bit flags
Expand Down
3 changes: 1 addition & 2 deletions stdlib/REPL/src/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -556,8 +556,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[])
return new(limit_aggressive_inference, world, inf_params, opt_params, inf_cache)
Expand Down
1 change: 0 additions & 1 deletion test/compiler/AbstractInterpreter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ end == Val{6}
@newinterp Issue48097Interp
@MethodTable ISSUE_48097_MT
CC.method_table(interp::Issue48097Interp) = CC.OverlayMethodTable(CC.get_inference_world(interp), ISSUE_48097_MT)
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,
Expand Down
2 changes: 1 addition & 1 deletion test/compiler/codegen.jl
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,7 @@ mktempdir() do pfx
libs_deleted += 1
end
@test libs_deleted > 0
@test readchomp(`$pfx/bin/$(Base.julia_exename()) -e 'print("no codegen!\n")'`) == "no codegen!"
@test readchomp(`$pfx/bin/$(Base.julia_exename()) --startup-file=no -e 'print("no codegen!\n")'`) == "no codegen!"

# PR #47343
libs_emptied = 0
Expand Down
4 changes: 2 additions & 2 deletions test/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1510,9 +1510,9 @@ end
for T in (Int, Float64, String, Symbol)
@testset let T=T
@test !Core.Compiler.is_consistent(Base.infer_effects(getindex, (Dict{T,Any}, T)))
@test_broken Core.Compiler.is_effect_free(Base.infer_effects(getindex, (Dict{T,Any}, T)))
@test Core.Compiler.is_effect_free(Base.infer_effects(getindex, (Dict{T,Any}, T)))
@test !Core.Compiler.is_nothrow(Base.infer_effects(getindex, (Dict{T,Any}, T)))
@test_broken Core.Compiler.is_terminates(Base.infer_effects(getindex, (Dict{T,Any}, T)))
@test Core.Compiler.is_terminates(Base.infer_effects(getindex, (Dict{T,Any}, T)))
end
end

Expand Down