Skip to content

Commit

Permalink
Merge branch 'master' into test_eltype_is_foldable
Browse files Browse the repository at this point in the history
  • Loading branch information
fingolfin authored Feb 2, 2025
2 parents ab90e76 + cee26a3 commit 653781a
Show file tree
Hide file tree
Showing 159 changed files with 2,094 additions and 1,169 deletions.
3 changes: 2 additions & 1 deletion Compiler/src/Compiler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ using Core: ABIOverride, Builtin, CodeInstance, IntrinsicFunction, MethodInstanc

using Base
using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospecializeinfer,
BINDING_KIND_GLOBAL, BINDING_KIND_UNDEF_CONST, Base, BitVector, Bottom, Callable, DataTypeFieldDesc,
BINDING_KIND_GLOBAL, BINDING_KIND_UNDEF_CONST, BINDING_KIND_BACKDATED_CONST,
Base, BitVector, Bottom, Callable, DataTypeFieldDesc,
EffectsOverride, Filter, Generator, IteratorSize, JLOptions, NUM_EFFECTS_OVERRIDES,
OneTo, Ordering, RefValue, SizeUnknown, _NAMEDTUPLE_NAME,
_array_for, _bits_findnext, _methods_by_ftype, _uniontypes, all, allocatedinline, any,
Expand Down
31 changes: 24 additions & 7 deletions Compiler/src/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ function find_union_split_method_matches(interp::AbstractInterpreter, argtypes::
for i in 1:length(split_argtypes)
arg_n = split_argtypes[i]::Vector{Any}
sig_n = argtypes_to_type(arg_n)
sig_n === Bottom && continue
mt = ccall(:jl_method_table_for, Any, (Any,), sig_n)
mt === nothing && return FailedMethodMatch("Could not identify method table for call")
mt = mt::MethodTable
Expand Down Expand Up @@ -614,7 +615,7 @@ function abstract_call_method(interp::AbstractInterpreter,
sigtuple = unwrap_unionall(sig)
sigtuple isa DataType ||
return Future(MethodCallResult(Any, Any, Effects(), nothing, false, false))
all(@nospecialize(x) -> valid_as_lattice(unwrapva(x), true), sigtuple.parameters) ||
all(@nospecialize(x) -> isvarargtype(x) || valid_as_lattice(x, true), sigtuple.parameters) ||
return Future(MethodCallResult(Union{}, Any, EFFECTS_THROWS, nothing, false, false)) # catch bad type intersections early

if is_nospecializeinfer(method)
Expand Down Expand Up @@ -2840,6 +2841,7 @@ function abstract_call_unknown(interp::AbstractInterpreter, @nospecialize(ft),
end
# non-constant function, but the number of arguments is known and the `f` is not a builtin or intrinsic
atype = argtypes_to_type(arginfo.argtypes)
atype === Bottom && return Future(CallMeta(Union{}, Union{}, EFFECTS_THROWS, NoCallInfo())) # accidentally unreachable
return abstract_call_gf_by_type(interp, nothing, arginfo, si, atype, sv, max_methods)::Future
end

Expand Down Expand Up @@ -3454,10 +3456,10 @@ world_range(ir::IRCode) = ir.valid_worlds
world_range(ci::CodeInfo) = WorldRange(ci.min_world, ci.max_world)
world_range(compact::IncrementalCompact) = world_range(compact.ir)

function force_binding_resolution!(g::GlobalRef)
function force_binding_resolution!(g::GlobalRef, world::UInt)
# Force resolution of the binding
# TODO: This will go away once we switch over to fully partitioned semantics
ccall(:jl_globalref_boundp, Cint, (Any,), g)
ccall(:jl_force_binding_resolution, Cvoid, (Any, Csize_t), g, world)
return nothing
end

Expand All @@ -3475,7 +3477,7 @@ function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode,
# This method is surprisingly hot. For performance, don't ask the runtime to resolve
# the binding unless necessary - doing so triggers an additional lookup, which though
# not super expensive is hot enough to show up in benchmarks.
force_binding_resolution!(g)
force_binding_resolution!(g, min_world(worlds))
return abstract_eval_globalref_type(g, src, false)
end
# return Union{}
Expand All @@ -3488,7 +3490,7 @@ function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode,
end

function lookup_binding_partition!(interp::AbstractInterpreter, g::GlobalRef, sv::AbsIntState)
force_binding_resolution!(g)
force_binding_resolution!(g, get_inference_world(interp))
partition = lookup_binding_partition(get_inference_world(interp), g)
update_valid_age!(sv, WorldRange(partition.min_world, partition.max_world))
partition
Expand Down Expand Up @@ -3522,6 +3524,11 @@ function abstract_eval_partition_load(interp::AbstractInterpreter, partition::Co
end

if is_defined_const_binding(kind)
if kind == BINDING_KIND_BACKDATED_CONST
# Infer this as guard. We do not want a later const definition to retroactively improve
# inference results in an earlier world.
return RTEffects(Any, UndefVarError, generic_getglobal_effects)
end
rt = Const(partition_restriction(partition))
return RTEffects(rt, Union{}, Effects(EFFECTS_TOTAL, inaccessiblememonly=is_mutation_free_argtype(rt) ? ALWAYS_TRUE : ALWAYS_FALSE))
end
Expand All @@ -3537,7 +3544,8 @@ function abstract_eval_globalref(interp::AbstractInterpreter, g::GlobalRef, saw_
partition = abstract_eval_binding_partition!(interp, g, sv)
ret = abstract_eval_partition_load(interp, partition)
if ret.rt !== Union{} && ret.exct === UndefVarError && InferenceParams(interp).assume_bindings_static
if isdefined(g, :binding) && isdefined(g.binding, :value)
b = convert(Core.Binding, g)
if isdefined(b, :value)
ret = RTEffects(ret.rt, Union{}, Effects(generic_getglobal_effects, nothrow=true))
end
# We do not assume in general that assigned global bindings remain assigned.
Expand Down Expand Up @@ -3785,14 +3793,23 @@ function update_bestguess!(interp::AbstractInterpreter, frame::InferenceState,
slottypes = frame.slottypes
rt = widenreturn(rt, BestguessInfo(interp, bestguess, nargs, slottypes, currstate))
# narrow representation of bestguess slightly to prepare for tmerge with rt
if rt isa InterConditional && bestguess isa Const
if rt isa InterConditional && bestguess isa Const && bestguess.val isa Bool
slot_id = rt.slot
old_id_type = widenconditional(slottypes[slot_id])
if bestguess.val === true && rt.elsetype !== Bottom
bestguess = InterConditional(slot_id, old_id_type, Bottom)
elseif bestguess.val === false && rt.thentype !== Bottom
bestguess = InterConditional(slot_id, Bottom, old_id_type)
end
# or narrow representation of rt slightly to prepare for tmerge with bestguess
elseif bestguess isa InterConditional && rt isa Const && rt.val isa Bool
slot_id = bestguess.slot
old_id_type = widenconditional(slottypes[slot_id])
if rt.val === true && bestguess.elsetype !== Bottom
rt = InterConditional(slot_id, old_id_type, Bottom)
elseif rt.val === false && bestguess.thentype !== Bottom
rt = InterConditional(slot_id, Bottom, old_id_type)
end
end
# copy limitations to return value
if !isempty(frame.pclimitations)
Expand Down
2 changes: 1 addition & 1 deletion Compiler/src/optimize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1286,7 +1286,7 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState)
# types of call arguments only once `slot2reg` converts this `IRCode` to the SSA form
# and eliminates slots (see below)
argtypes = sv.slottypes
return IRCode(stmts, sv.cfg, di, argtypes, meta, sv.sptypes, WorldRange(ci.min_world, ci.max_world))
return IRCode(stmts, sv.cfg, di, argtypes, meta, sv.sptypes, world_range(ci))
end

function process_meta!(meta::Vector{Expr}, @nospecialize stmt)
Expand Down
1 change: 1 addition & 0 deletions Compiler/src/ssair/inlining.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1399,6 +1399,7 @@ function handle_call!(todo::Vector{Pair{Int,Any}},
cases === nothing && return nothing
cases, handled_all_cases, fully_covered, joint_effects = cases
atype = argtypes_to_type(sig.argtypes)
atype === Union{} && return nothing # accidentally actually unreachable
handle_cases!(todo, ir, idx, stmt, atype, cases, handled_all_cases, fully_covered, joint_effects)
end

Expand Down
4 changes: 2 additions & 2 deletions Compiler/src/ssair/ir.jl
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ struct IRCode
function IRCode(stmts::InstructionStream, cfg::CFG, debuginfo::DebugInfoStream,
argtypes::Vector{Any}, meta::Vector{Expr}, sptypes::Vector{VarState},
valid_worlds=WorldRange(typemin(UInt), typemax(UInt)))
return new(stmts, argtypes, sptypes, debuginfo, cfg, NewNodeStream(), meta)
return new(stmts, argtypes, sptypes, debuginfo, cfg, NewNodeStream(), meta, valid_worlds)
end
function IRCode(ir::IRCode, stmts::InstructionStream, cfg::CFG, new_nodes::NewNodeStream)
di = ir.debuginfo
Expand Down Expand Up @@ -1462,7 +1462,7 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr
result[result_idx][:stmt] = GotoNode(label)
result_idx += 1
elseif isa(stmt, GlobalRef)
total_flags = IR_FLAG_CONSISTENT | IR_FLAG_EFFECT_FREE
total_flags = IR_FLAG_CONSISTENT | IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW
flag = result[result_idx][:flag]
if has_flag(flag, total_flags)
ssa_rename[idx] = stmt
Expand Down
2 changes: 1 addition & 1 deletion Compiler/src/ssair/legacy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function inflate_ir!(ci::CodeInfo, sptypes::Vector{VarState}, argtypes::Vector{A
di = DebugInfoStream(nothing, ci.debuginfo, nstmts)
stmts = InstructionStream(code, ssavaluetypes, info, di.codelocs, ci.ssaflags)
meta = Expr[]
return IRCode(stmts, cfg, di, argtypes, meta, sptypes, WorldRange(ci.min_world, ci.max_world))
return IRCode(stmts, cfg, di, argtypes, meta, sptypes, world_range(ci))
end

"""
Expand Down
4 changes: 0 additions & 4 deletions Compiler/src/ssair/slot2ssa.jl
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,6 @@ function fixemup!(@specialize(slot_filter), @specialize(rename_slot), ir::IRCode
return nothing
end
op[] = x
elseif isa(val, GlobalRef) && !(isdefined(val.mod, val.name) && isconst(val.mod, val.name))
typ = typ_for_val(val, ci, ir, idx, Any[])
new_inst = NewInstruction(val, typ)
op[] = NewSSAValue(insert_node!(ir, idx, new_inst).id - length(ir.stmts))
elseif isexpr(val, :static_parameter)
ty = typ_for_val(val, ci, ir, idx, Any[])
if isa(ty, Const)
Expand Down
23 changes: 19 additions & 4 deletions Compiler/src/ssair/verify.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ end

if !isdefined(@__MODULE__, Symbol("@verify_error"))
macro verify_error(arg)
arg isa String && return esc(:(print && println(stderr, $arg)))
arg isa String && return esc(:(print && println($(GlobalRef(Core, :stderr)), $arg)))
isexpr(arg, :string) || error("verify_error macro expected a string expression")
pushfirst!(arg.args, GlobalRef(Core, :stderr))
pushfirst!(arg.args, :println)
Expand Down Expand Up @@ -61,8 +61,14 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int,
raise_error()
end
elseif isa(op, GlobalRef)
if !isdefined(op.mod, op.name) || !isconst(op.mod, op.name)
@verify_error "Unbound GlobalRef not allowed in value position"
force_binding_resolution!(op, min_world(ir.valid_worlds))
bpart = lookup_binding_partition(min_world(ir.valid_worlds), op)
while is_some_imported(binding_kind(bpart)) && max_world(ir.valid_worlds) <= bpart.max_world
imported_binding = partition_restriction(bpart)::Core.Binding
bpart = lookup_binding_partition(min_world(ir.valid_worlds), imported_binding)
end
if !is_defined_const_binding(binding_kind(bpart)) || (bpart.max_world < max_world(ir.valid_worlds))
@verify_error "Unbound or partitioned GlobalRef not allowed in value position"
raise_error()
end
elseif isa(op, Expr)
Expand Down Expand Up @@ -115,7 +121,7 @@ function verify_ir(ir::IRCode, print::Bool=true,
if mi !== nothing
push!(error_args, "\n", " Method instance: ", mi)
end
error(error_args...)
invokelatest(error, error_args...)
end
# Verify CFG graph. Must be well formed to construct domtree
if !(length(ir.cfg.blocks) - 1 <= length(ir.cfg.index) <= length(ir.cfg.blocks))
Expand Down Expand Up @@ -374,6 +380,15 @@ function verify_ir(ir::IRCode, print::Bool=true,
# undefined GlobalRef is OK in isdefined
continue
end
elseif stmt.head === :throw_undef_if_not
if length(stmt.args) > 3
@verify_error "malformed throw_undef_if_not"
raise_error()
end
if stmt.args[1] isa GlobalRef
# undefined GlobalRef is OK in throw_undef_if_not
continue
end
elseif stmt.head === :gc_preserve_end
# We allow gc_preserve_end tokens to span across try/catch
# blocks, which isn't allowed for regular SSA values, so
Expand Down
38 changes: 21 additions & 17 deletions Compiler/src/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1109,7 +1109,7 @@ function _getfield_tfunc_const(@nospecialize(sv), name::Const)
if isa(sv, DataType) && nv == DATATYPE_TYPES_FIELDINDEX && isdefined(sv, nv)
return Const(getfield(sv, nv))
end
if isconst(typeof(sv), nv)
if !isa(sv, Module) && isconst(typeof(sv), nv)
if isdefined(sv, nv)
return Const(getfield(sv, nv))
end
Expand Down Expand Up @@ -3016,24 +3016,28 @@ function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any},
isvarargtype(argtypes[2]) && return Future(CallMeta(Bool, ArgumentError, EFFECTS_THROWS, NoCallInfo()))
argtypes = argtypes[2:end]
atype = argtypes_to_type(argtypes)
matches = find_method_matches(interp, argtypes, atype; max_methods)
info = NoCallInfo()
if isa(matches, FailedMethodMatch)
rt = Bool # too many matches to analyze
if atype === Union{}
rt = Union{} # accidentally unreachable code
else
(; valid_worlds, applicable) = matches
update_valid_age!(sv, valid_worlds)
napplicable = length(applicable)
if napplicable == 0
rt = Const(false) # never any matches
elseif !fully_covering(matches) || any_ambig(matches)
# Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature.
rt = Bool
matches = find_method_matches(interp, argtypes, atype; max_methods)
info = NoCallInfo()
if isa(matches, FailedMethodMatch)
rt = Bool # too many matches to analyze
else
rt = Const(true) # has applicable matches
end
if rt !== Bool
info = VirtualMethodMatchInfo(matches.info)
(; valid_worlds, applicable) = matches
update_valid_age!(sv, valid_worlds)
napplicable = length(applicable)
if napplicable == 0
rt = Const(false) # never any matches
elseif !fully_covering(matches) || any_ambig(matches)
# Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature.
rt = Bool
else
rt = Const(true) # has applicable matches
end
if rt !== Bool
info = VirtualMethodMatchInfo(matches.info)
end
end
end
return Future(CallMeta(rt, Union{}, EFFECTS_TOTAL, info))
Expand Down
16 changes: 14 additions & 2 deletions Compiler/src/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1280,8 +1280,20 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim::
ci isa CodeInstance && !use_const_api(ci) && push!(tocompile, ci)
end
elseif item isa SimpleVector
push!(codeinfos, item[1]::Type)
push!(codeinfos, item[2]::Type)
(rt::Type, sig::Type) = item
# make a best-effort attempt to enqueue the relevant code for the ccallable
ptr = ccall(:jl_get_specialization1,
#= MethodInstance =# Ptr{Cvoid}, (Any, Csize_t, Cint),
sig, this_world, #= mt_cache =# 0)
if ptr !== C_NULL
mi = unsafe_pointer_to_objref(ptr)
ci = typeinf_ext(interp, mi, SOURCE_MODE_NOT_REQUIRED)
ci isa CodeInstance && !use_const_api(ci) && push!(tocompile, ci)
end
# additionally enqueue the ccallable entrypoint / adapter, which implicitly
# invokes the above ci
push!(codeinfos, rt)
push!(codeinfos, sig)
end
end
while !isempty(tocompile)
Expand Down
7 changes: 7 additions & 0 deletions Compiler/src/typelattice.jl
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,13 @@ end
end
a = Bool
elseif isa(b, ConditionalT)
if isa(a, Const) && isa(a.val, Bool)
if (a.val === true && b.thentype === Any && b.elsetype === Bottom) ||
(a.val === false && b.elsetype === Any && b.thentype === Bottom)
# this Conditional contains distinctly no lattice information, and is simply an alternative representation of the Const Bool used for internal tracking purposes
return true
end
end
return false
end
return (widenlattice(lattice), a, b)
Expand Down
7 changes: 6 additions & 1 deletion Compiler/src/typeutils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ has_extended_info(@nospecialize x) = (!isa(x, Type) && !isvarargtype(x)) || isTy
# certain combinations of `a` and `b` where one/both isa/are `Union`/`UnionAll` type(s)s.
isnotbrokensubtype(@nospecialize(a), @nospecialize(b)) = (!iskindtype(b) || !isType(a) || hasuniquerep(a.parameters[1]) || b <: a)

argtypes_to_type(argtypes::Array{Any,1}) = Tuple{anymap(@nospecialize(a) -> isvarargtype(a) ? a : widenconst(a), argtypes)...}
function argtypes_to_type(argtypes::Array{Any,1})
argtypes = anymap(@nospecialize(a) -> isvarargtype(a) ? a : widenconst(a), argtypes)
filter!(@nospecialize(x) -> !isvarargtype(x) || valid_as_lattice(unwrapva(x), true), argtypes)
all(@nospecialize(x) -> isvarargtype(x) || valid_as_lattice(x, true), argtypes) || return Bottom
return Tuple{argtypes...}
end

function isknownlength(t::DataType)
isvatuple(t) || return true
Expand Down
3 changes: 2 additions & 1 deletion Compiler/test/abioverride.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ let world = Base.tls_world_age()
## Remove the argument
resize!(new_source.slotnames, 2)
resize!(new_source.slotflags, 2)
new_source.nargs = 2

# Construct the CodeInstance from the modified CodeInfo data
global new_ci = Core.CodeInstance(Core.ABIOverride(Tuple{typeof(myplus), Int}, mi),
#=owner=#SecondArgConstOverride(1), new_source.rettype, Any#=new_source.exctype is missing=#,
#=inferred_const=#nothing, #=code=#nothing, #=const_flags=#Int32(0),
new_source.min_world, new_source.max_world, #=new_source.ipo_purity_bits is missing=#UInt32(0),
new_source.min_world, typemax(UInt), #=new_source.ipo_purity_bits is missing=#UInt32(0),
#=analysis_results=#nothing, new_source.debuginfo, new_source.edges)

# Poke the CI into the global cache
Expand Down
19 changes: 18 additions & 1 deletion Compiler/test/codegen.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,7 @@ end
# Make sure that code that has unbound sparams works
#https://github.com/JuliaLang/julia/issues/56739

f56739(a) where {T} = a
@test_warn r"declares type variable T but does not use it" @eval f56739(a) where {T} = a

@test f56739(1) == 1
g56739(x) = @noinline f56739(x)
Expand All @@ -1041,3 +1041,20 @@ struct Vec56937 x::NTuple{8, VecElement{Int}} end

x56937 = Ref(Vec56937(ntuple(_->VecElement(1),8)))
@test x56937[].x[1] == VecElement{Int}(1) # shouldn't crash

# issue #56996
let
()->() # trigger various heuristics
Base.Experimental.@force_compile
default_rng_orig = [] # make a value in a Slot
try
# overwrite the gc-slots in the exception branch
throw(ErrorException("This test is supposed to throw an error"))
catch ex
# destroy any values that aren't referenced
GC.gc()
# make sure that default_rng_orig value is still valid
@noinline copy!([], default_rng_orig)
end
nothing
end
Loading

0 comments on commit 653781a

Please sign in to comment.