Skip to content

Commit 0c6ee07

Browse files
authored
compiler: minor inference adjustments (JuliaLang#54420)
This PR consists of a collection of minor adjustments to inference. I've tried to ensure that the purpose of each change is communicated by each commit message. - tfuncs: cosmetic changes (63bdfe0) * rm no longer necessary type assertions * rm extra newlines * mk some indents more consistent with the others - tfuncs: refactor common `varargtype` check into `hasvarargtype` (b92adff) - tfuncs: improve nothrow modeling of `tuple` and `svec` (9689fc0) - inlining: replace `[builtin|intrinsic]_nothrow` checks with flag-base ones (ee736ae) - tfuncs: rm the weird `UnionAll` special case (f2f6256)
2 parents 62b5159 + 3197075 commit 0c6ee07

File tree

4 files changed

+50
-59
lines changed

4 files changed

+50
-59
lines changed

base/compiler/optimize.jl

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -288,12 +288,7 @@ function new_expr_effect_flags(𝕃ₒ::AbstractLattice, args::Vector{Any}, src:
288288
return (false, true, true)
289289
end
290290

291-
"""
292-
stmt_effect_flags(stmt, rt, src::Union{IRCode,IncrementalCompact}) ->
293-
(consistent::Bool, removable::Bool, nothrow::Bool)
294-
295-
Returns a tuple of `(:consistent, :removable, :nothrow)` flags for a given statement.
296-
"""
291+
# Returns a tuple of `(:consistent, :removable, :nothrow)` flags for a given statement.
297292
function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospecialize(rt), src::Union{IRCode,IncrementalCompact})
298293
# TODO: We're duplicating analysis from inference here.
299294
isa(stmt, PiNode) && return (true, true, true)
@@ -317,12 +312,6 @@ function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospe
317312
f = argextype(args[1], src)
318313
f = singleton_type(f)
319314
f === nothing && return (false, false, false)
320-
if f === UnionAll
321-
# TODO: This is a weird special case - should be determined in inference
322-
argtypes = Any[argextype(args[arg], src) for arg in 2:length(args)]
323-
nothrow = _builtin_nothrow(𝕃ₒ, f, argtypes, rt)
324-
return (true, nothrow, nothrow)
325-
end
326315
if f === Intrinsics.cglobal || f === Intrinsics.llvmcall
327316
# TODO: these are not yet linearized
328317
return (false, false, false)

base/compiler/ssair/inlining.jl

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,7 +1226,8 @@ end
12261226
# Handles all analysis and inlining of intrinsics and builtins. In particular,
12271227
# this method does not access the method table or otherwise process generic
12281228
# functions.
1229-
function process_simple!(todo::Vector{Pair{Int,Any}}, ir::IRCode, idx::Int, state::InliningState)
1229+
function process_simple!(todo::Vector{Pair{Int,Any}}, ir::IRCode, idx::Int, flag::UInt32,
1230+
state::InliningState)
12301231
inst = ir[SSAValue(idx)]
12311232
stmt = inst[:stmt]
12321233
if !(stmt isa Expr)
@@ -1257,7 +1258,7 @@ function process_simple!(todo::Vector{Pair{Int,Any}}, ir::IRCode, idx::Int, stat
12571258
sig === nothing && return nothing
12581259

12591260
# Check if we match any of the early inliners
1260-
earlyres = early_inline_special_case(ir, stmt, rt, sig, state)
1261+
earlyres = early_inline_special_case(ir, stmt, flag, rt, sig, state)
12611262
if isa(earlyres, SomeCase)
12621263
inst[:stmt] = earlyres.val
12631264
return nothing
@@ -1286,7 +1287,7 @@ function process_simple!(todo::Vector{Pair{Int,Any}}, ir::IRCode, idx::Int, stat
12861287
end
12871288

12881289
# Special case inliners for regular functions
1289-
lateres = late_inline_special_case!(ir, idx, stmt, rt, sig, state)
1290+
lateres = late_inline_special_case!(ir, idx, stmt, flag, rt, sig, state)
12901291
if isa(lateres, SomeCase)
12911292
inst[:stmt] = lateres.val
12921293
add_inst_flag!(inst, ir, state)
@@ -1616,11 +1617,12 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState)
16161617
todo = Pair{Int, Any}[]
16171618

16181619
for idx in 1:length(ir.stmts)
1619-
simpleres = process_simple!(todo, ir, idx, state)
1620+
flag = ir.stmts[idx][:flag]
1621+
1622+
simpleres = process_simple!(todo, ir, idx, flag, state)
16201623
simpleres === nothing && continue
16211624
stmt, sig = simpleres
16221625

1623-
flag = ir.stmts[idx][:flag]
16241626
info = ir.stmts[idx][:info]
16251627

16261628
# `NativeInterpreter` won't need this, but provide a support for `:invoke` exprs here
@@ -1666,23 +1668,22 @@ function linear_inline_eligible(ir::IRCode)
16661668
return true
16671669
end
16681670

1669-
function early_inline_special_case(
1670-
ir::IRCode, stmt::Expr, @nospecialize(type), sig::Signature,
1671-
state::InliningState)
1671+
function early_inline_special_case(ir::IRCode, stmt::Expr, flag::UInt32,
1672+
@nospecialize(type), sig::Signature, state::InliningState)
16721673
OptimizationParams(state.interp).inlining || return nothing
16731674
(; f, ft, argtypes) = sig
16741675

16751676
if isa(type, Const) # || isconstType(type)
16761677
val = type.val
16771678
is_inlineable_constant(val) || return nothing
16781679
if isa(f, IntrinsicFunction)
1679-
if is_pure_intrinsic_infer(f) && intrinsic_nothrow(f, argtypes[2:end])
1680+
if is_pure_intrinsic_infer(f) && has_flag(flag, IR_FLAG_NOTHROW)
16801681
return SomeCase(quoted(val))
16811682
end
16821683
elseif contains_is(_PURE_BUILTINS, f)
16831684
return SomeCase(quoted(val))
16841685
elseif contains_is(_EFFECT_FREE_BUILTINS, f)
1685-
if _builtin_nothrow(optimizer_lattice(state.interp), f, argtypes[2:end], type)
1686+
if has_flag(flag, IR_FLAG_NOTHROW)
16861687
return SomeCase(quoted(val))
16871688
end
16881689
elseif f === Core.get_binding_type
@@ -1723,9 +1724,8 @@ end
17231724
# special-case some regular method calls whose results are not folded within `abstract_call_known`
17241725
# (and thus `early_inline_special_case` doesn't handle them yet)
17251726
# NOTE we manually inline the method bodies, and so the logic here needs to precisely sync with their definitions
1726-
function late_inline_special_case!(
1727-
ir::IRCode, idx::Int, stmt::Expr, @nospecialize(type), sig::Signature,
1728-
state::InliningState)
1727+
function late_inline_special_case!(ir::IRCode, idx::Int, stmt::Expr, flag::UInt32,
1728+
@nospecialize(type), sig::Signature, state::InliningState)
17291729
OptimizationParams(state.interp).inlining || return nothing
17301730
(; f, ft, argtypes) = sig
17311731
if length(argtypes) == 3 && istopfunction(f, :!==)
@@ -1741,7 +1741,7 @@ function late_inline_special_case!(
17411741
elseif length(argtypes) == 3 && istopfunction(f, :(>:))
17421742
# special-case inliner for issupertype
17431743
# that works, even though inference generally avoids inferring the `>:` Method
1744-
if isa(type, Const) && _builtin_nothrow(optimizer_lattice(state.interp), <:, Any[argtypes[3], argtypes[2]], type)
1744+
if isa(type, Const) && has_flag(flag, IR_FLAG_NOTHROW)
17451745
return SomeCase(quoted(type.val))
17461746
end
17471747
subtype_call = Expr(:call, GlobalRef(Core, :(<:)), stmt.args[3], stmt.args[2])

base/compiler/tfuncs.jl

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1994,7 +1994,6 @@ add_tfunc(Core.memoryrefmodify!, 5, 5, memoryrefmodify!_tfunc, 20)
19941994
add_tfunc(Core.memoryrefreplace!, 6, 6, memoryrefreplace!_tfunc, 20)
19951995
add_tfunc(Core.memoryrefsetonce!, 5, 5, memoryrefsetonce!_tfunc, 20)
19961996

1997-
19981997
@nospecs function memoryref_isassigned_tfunc(𝕃::AbstractLattice, mem, order, boundscheck)
19991998
return _memoryref_isassigned_tfunc(𝕃, mem, order, boundscheck)
20001999
end
@@ -2176,9 +2175,12 @@ end
21762175
return boundscheck Bool && memtype GenericMemoryRef && order Symbol
21772176
end
21782177

2179-
# Query whether the given builtin is guaranteed not to throw given the argtypes
2180-
@nospecs function _builtin_nothrow(𝕃::AbstractLattice, f, argtypes::Vector{Any}, rt)
2178+
# Query whether the given builtin is guaranteed not to throw given the `argtypes`.
2179+
# `argtypes` can be assumed not to contain varargs.
2180+
function _builtin_nothrow(𝕃::AbstractLattice, @nospecialize(f::Builtin), argtypes::Vector{Any},
2181+
@nospecialize(rt))
21812182
= partialorder(𝕃)
2183+
na = length(argtypes)
21822184
if f === memoryref
21832185
return memoryref_builtin_common_nothrow(argtypes)
21842186
elseif f === memoryrefoffset
@@ -2194,13 +2196,7 @@ end
21942196
elseif f === Core._expr
21952197
length(argtypes) >= 1 || return false
21962198
return argtypes[1] Symbol
2197-
end
2198-
2199-
# These builtins are not-vararg, so if we have varars, here, we can't guarantee
2200-
# the correct number of arguments.
2201-
na = length(argtypes)
2202-
(na 0 && isvarargtype(argtypes[end])) && return false
2203-
if f === Core._typevar
2199+
elseif f === Core._typevar
22042200
na == 3 || return false
22052201
return typevar_nothrow(𝕃, argtypes[1], argtypes[2], argtypes[3])
22062202
elseif f === invoke
@@ -2225,8 +2221,6 @@ end
22252221
elseif f === (<:)
22262222
na == 2 || return false
22272223
return subtype_nothrow(𝕃, argtypes[1], argtypes[2])
2228-
elseif f === UnionAll
2229-
return na == 2 && (argtypes[1] TypeVar && argtypes[2] Type)
22302224
elseif f === isdefined
22312225
return isdefined_nothrow(𝕃, argtypes)
22322226
elseif f === Core.sizeof
@@ -2334,7 +2328,7 @@ const _INACCESSIBLEMEM_BUILTINS = Any[
23342328
typeof,
23352329
compilerbarrier,
23362330
Core._typevar,
2337-
donotdelete
2331+
donotdelete,
23382332
]
23392333

23402334
const _ARGMEM_BUILTINS = Any[
@@ -2505,8 +2499,7 @@ function builtin_effects(𝕃::AbstractLattice, @nospecialize(f::Builtin), argty
25052499
return Effects(EFFECTS_TOTAL;
25062500
consistent = ALWAYS_FALSE,
25072501
notaskstate = false,
2508-
nothrow
2509-
)
2502+
nothrow)
25102503
else
25112504
if contains_is(_CONSISTENT_BUILTINS, f)
25122505
consistent = ALWAYS_TRUE
@@ -2526,7 +2519,7 @@ function builtin_effects(𝕃::AbstractLattice, @nospecialize(f::Builtin), argty
25262519
else
25272520
effect_free = ALWAYS_FALSE
25282521
end
2529-
nothrow = (isempty(argtypes) || !isvarargtype(argtypes[end])) && builtin_nothrow(𝕃, f, argtypes, rt)
2522+
nothrow = builtin_nothrow(𝕃, f, argtypes, rt)
25302523
if contains_is(_INACCESSIBLEMEM_BUILTINS, f)
25312524
inaccessiblememonly = ALWAYS_TRUE
25322525
elseif contains_is(_ARGMEM_BUILTINS, f)
@@ -2543,7 +2536,6 @@ function builtin_effects(𝕃::AbstractLattice, @nospecialize(f::Builtin), argty
25432536
end
25442537
end
25452538

2546-
25472539
function memoryop_noub(@nospecialize(f), argtypes::Vector{Any})
25482540
nargs = length(argtypes)
25492541
nargs == 0 && return true # must throw and noub
@@ -2600,14 +2592,22 @@ function current_scope_tfunc(interp::AbstractInterpreter, sv::InferenceState)
26002592
end
26012593
current_scope_tfunc(interp::AbstractInterpreter, sv) = Any
26022594

2595+
hasvarargtype(argtypes::Vector{Any}) = !isempty(argtypes) && isvarargtype(argtypes[end])
2596+
26032597
"""
26042598
builtin_nothrow(𝕃::AbstractLattice, f::Builtin, argtypes::Vector{Any}, rt) -> Bool
26052599
26062600
Compute throw-ness of a builtin function call. `argtypes` should not include `f` itself.
26072601
"""
26082602
function builtin_nothrow(𝕃::AbstractLattice, @nospecialize(f), argtypes::Vector{Any}, @nospecialize(rt))
26092603
rt === Bottom && return false
2610-
contains_is(_PURE_BUILTINS, f) && return true
2604+
if f === tuple || f === svec
2605+
return true
2606+
elseif hasvarargtype(argtypes)
2607+
return false
2608+
elseif contains_is(_PURE_BUILTINS, f)
2609+
return true
2610+
end
26112611
return _builtin_nothrow(𝕃, f, argtypes, rt)
26122612
end
26132613

@@ -2632,7 +2632,7 @@ function builtin_tfunction(interp::AbstractInterpreter, @nospecialize(f), argtyp
26322632
return Bottom
26332633
end
26342634
end
2635-
iidx = Int(reinterpret(Int32, f::IntrinsicFunction)) + 1
2635+
iidx = Int(reinterpret(Int32, f)) + 1
26362636
if iidx < 0 || iidx > length(T_IFUNC)
26372637
# unknown intrinsic
26382638
return Any
@@ -2656,8 +2656,7 @@ function builtin_tfunction(interp::AbstractInterpreter, @nospecialize(f), argtyp
26562656
end
26572657
tf = T_FFUNC_VAL[fidx]
26582658
end
2659-
tf = tf::Tuple{Int, Int, Any}
2660-
if !isempty(argtypes) && isvarargtype(argtypes[end])
2659+
if hasvarargtype(argtypes)
26612660
if length(argtypes) - 1 > tf[2]
26622661
# definitely too many arguments
26632662
return Bottom
@@ -2687,8 +2686,6 @@ _iszero(@nospecialize x) = x === Intrinsics.xor_int(x, x)
26872686
_isneg1(@nospecialize x) = _iszero(Intrinsics.not_int(x))
26882687
_istypemin(@nospecialize x) = !_iszero(x) && Intrinsics.neg_int(x) === x
26892688

2690-
2691-
26922689
function builtin_exct(𝕃::AbstractLattice, @nospecialize(f::Builtin), argtypes::Vector{Any}, @nospecialize(rt))
26932690
if isa(f, IntrinsicFunction)
26942691
return intrinsic_exct(𝕃, f, argtypes)
@@ -2698,7 +2695,6 @@ end
26982695

26992696
function div_nothrow(f::IntrinsicFunction, @nospecialize(arg1), @nospecialize(arg2))
27002697
isa(arg2, Const) || return false
2701-
27022698
den_val = arg2.val
27032699
_iszero(den_val) && return false
27042700
f !== Intrinsics.checked_sdiv_int && return true
@@ -2713,18 +2709,17 @@ function known_is_valid_intrinsic_elptr(𝕃::AbstractLattice, @nospecialize(ptr
27132709
end
27142710

27152711
function intrinsic_exct(𝕃::AbstractLattice, f::IntrinsicFunction, argtypes::Vector{Any})
2716-
if !isempty(argtypes) && isvarargtype(argtypes[end])
2712+
if hasvarargtype(argtypes)
27172713
return Any
27182714
end
27192715

27202716
# First check that we have the correct number of arguments
2721-
iidx = Int(reinterpret(Int32, f::IntrinsicFunction)) + 1
2717+
iidx = Int(reinterpret(Int32, f)) + 1
27222718
if iidx < 1 || iidx > length(T_IFUNC)
27232719
# invalid intrinsic (system will crash)
27242720
return Any
27252721
end
27262722
tf = T_IFUNC[iidx]
2727-
tf = tf::Tuple{Int, Int, Any}
27282723
if !(tf[1] <= length(argtypes) <= tf[2])
27292724
# wrong # of args
27302725
return ArgumentError
@@ -2736,7 +2731,8 @@ function intrinsic_exct(𝕃::AbstractLattice, f::IntrinsicFunction, argtypes::V
27362731
# that it won't
27372732
f === Intrinsics.llvmcall && return Any
27382733

2739-
if f === Intrinsics.checked_udiv_int || f === Intrinsics.checked_urem_int || f === Intrinsics.checked_srem_int || f === Intrinsics.checked_sdiv_int
2734+
if (f === Intrinsics.checked_udiv_int || f === Intrinsics.checked_urem_int ||
2735+
f === Intrinsics.checked_srem_int || f === Intrinsics.checked_sdiv_int)
27402736
# Nothrow as long as the second argument is guaranteed not to be zero
27412737
arg1 = argtypes[1]
27422738
arg2 = argtypes[2]
@@ -2788,8 +2784,8 @@ function intrinsic_exct(𝕃::AbstractLattice, f::IntrinsicFunction, argtypes::V
27882784
end
27892785

27902786
if f in (Intrinsics.sext_int, Intrinsics.zext_int, Intrinsics.trunc_int,
2791-
Intrinsics.fptoui, Intrinsics.fptosi, Intrinsics.uitofp,
2792-
Intrinsics.sitofp, Intrinsics.fptrunc, Intrinsics.fpext)
2787+
Intrinsics.fptoui, Intrinsics.fptosi, Intrinsics.uitofp,
2788+
Intrinsics.sitofp, Intrinsics.fptrunc, Intrinsics.fpext)
27932789
# If !isconcrete, `ty` may be Union{} at runtime even if we have
27942790
# isprimitivetype(ty).
27952791
ty, isexact, isconcrete = instanceof_tfunc(argtypes[1], true)
@@ -2851,14 +2847,13 @@ function intrinsic_effects(f::IntrinsicFunction, argtypes::Vector{Any})
28512847
# llvmcall can do arbitrary things
28522848
return Effects()
28532849
end
2854-
28552850
if contains_is(_INCONSISTENT_INTRINSICS, f)
28562851
consistent = ALWAYS_FALSE
28572852
else
28582853
consistent = ALWAYS_TRUE
28592854
end
28602855
effect_free = !(f === Intrinsics.pointerset) ? ALWAYS_TRUE : ALWAYS_FALSE
2861-
nothrow = (isempty(argtypes) || !isvarargtype(argtypes[end])) && intrinsic_nothrow(f, argtypes)
2856+
nothrow = intrinsic_nothrow(f, argtypes)
28622857
inaccessiblememonly = ALWAYS_TRUE
28632858
return Effects(EFFECTS_TOTAL; consistent, effect_free, nothrow, inaccessiblememonly)
28642859
end
@@ -3103,7 +3098,6 @@ add_tfunc(Core.modifyglobal!, 4, 5, modifyglobal!_tfunc, 3)
31033098
add_tfunc(Core.replaceglobal!, 4, 6, replaceglobal!_tfunc, 3)
31043099
add_tfunc(Core.setglobalonce!, 3, 5, setglobalonce!_tfunc, 3)
31053100

3106-
31073101
@nospecs function setglobal!_nothrow(M, s, newty, o)
31083102
global_order_nothrow(o, #=loading=#false, #=storing=#true) || return false
31093103
return setglobal!_nothrow(M, s, newty)

test/compiler/effects.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,3 +1353,11 @@ global v53613 = nothing
13531353
@test f53613() === nothing
13541354
@test g53613() === nothing
13551355
@test h53613() === nothing
1356+
1357+
# tuple/svec effects
1358+
@test Base.infer_effects((Vector{Any},)) do xs
1359+
Core.tuple(xs...)
1360+
end |> Core.Compiler.is_nothrow
1361+
@test Base.infer_effects((Vector{Any},)) do xs
1362+
Core.svec(xs...)
1363+
end |> Core.Compiler.is_nothrow

0 commit comments

Comments
 (0)