Skip to content

Commit c8a5046

Browse files
committed
inference: make throw block deoptimization concrete-eval friendly
The deoptimization can sometimes destroy the effects analysis and disable [semi-]concrete evaluation that is otherwise possible. This is because the deoptimization was designed with the type domain profitability in mind (#35982), and it has not been aware of the effects domain very well. This commit makes the deoptimization aware of the effects domain more and enables the `throw` block deoptimization only when the effects already known to be ineligible for concrete-evaluation. In our current effect system, `ALWAYS_FALSE`/`false` means that the effect can not be refined to `ALWAYS_TRUE`/`true` anymore (unless given user annotation later). Therefore we can enable the `throw` block deoptimization without hindering the chance of concrete-evaluation when any of the following conditions are met: - `effects.consistent === ALWAYS_FALSE` - `effects.effect_free === ALWAYS_FALSE` - `effects.terminates` - `effects.nonoverlayed` ``` Here are some numbers: | Metric | master | this commit | #35982 reverted (set `unoptimize_throw_blocks=false`) | |-------------------------|-----------|-------------|--------------------------------------------| | Base (seconds) | 15.579300 | 15.206645 | 15.296319 | | Stdlibs (seconds) | 17.919013 | 17.667094 | 17.738128 | | Total (seconds) | 33.499279 | 32.874737 | 33.035448 | | Precompilation (seconds) | 49.967516 | 49.421121 | 49.999998 | | First time `plot(rand(10,3))` [^1] | `2.476678 seconds (11.74 M allocations)` | `2.430355 seconds (11.77 M allocations)` | `2.514874 seconds (11.64 M allocations)` | [^1]: I got these numbers with disabling all the `@precompile_all_calls` statements in Plots.jl. These numbers made me question if we are getting any actual benefit from the `throw` block deoptimization anymore. Since it is sometimes harmful for the effects analysis, we probably want to either merge this commit or remove the `throw` block deoptimization completely.
1 parent 5fc5556 commit c8a5046

File tree

2 files changed

+24
-2
lines changed

2 files changed

+24
-2
lines changed

base/compiler/inferencestate.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -874,8 +874,14 @@ function should_infer_this_call(interp::AbstractInterpreter, sv::InferenceState)
874874
return true
875875
end
876876
function should_infer_for_effects(sv::InferenceState)
877+
def = sv.linfo.def
878+
def isa Method || return false # toplevel frame will not be [semi-]concrete-evaluated
877879
effects = sv.ipo_effects
878-
return is_terminates(effects) && is_effect_free(effects)
880+
override = decode_effects_override(def.purity)
881+
effects.consistent === ALWAYS_FALSE && !is_effect_overridden(override, :consistent) && return false
882+
effects.effect_free === ALWAYS_FALSE && !is_effect_overridden(override, :effect_free) && return false
883+
!effects.terminates && !is_effect_overridden(override, :terminates_globally) && return false
884+
return true
879885
end
880886
should_infer_this_call(::AbstractInterpreter, ::IRInterpretationState) = true
881887

test/compiler/effects.jl

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -892,7 +892,7 @@ end |> Core.Compiler.is_foldable
892892
getfield(w, s)
893893
end |> Core.Compiler.is_foldable
894894

895-
# Flow-sensitive consistenct for _typevar
895+
# Flow-sensitive consistent for _typevar
896896
@test Base.infer_effects() do
897897
return WrapperOneField == (WrapperOneField{T} where T)
898898
end |> Core.Compiler.is_foldable_nothrow
@@ -1001,6 +1001,22 @@ isassigned_effects(s) = isassigned(Ref(s))
10011001
isassigned_effects(:foo)
10021002
end
10031003

1004+
# inference on throw block should be disabled only when the effects are already known to be
1005+
# concrete-eval ineligible:
1006+
function optimize_throw_block_for_effects(x)
1007+
a = [x]
1008+
if x < 0
1009+
throw(ArgumentError(lazy"negative number given: $x"))
1010+
end
1011+
return a
1012+
end
1013+
let effects = Base.infer_effects(optimize_throw_block_for_effects, (Int,))
1014+
@test Core.Compiler.is_consistent_if_notreturned(effects)
1015+
@test Core.Compiler.is_effect_free(effects)
1016+
@test !Core.Compiler.is_nothrow(effects)
1017+
@test Core.Compiler.is_terminates(effects)
1018+
end
1019+
10041020
# :isdefined effects
10051021
@test @eval Base.infer_effects() do
10061022
@isdefined($(gensym("some_undef_symbol")))

0 commit comments

Comments
 (0)