diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index 2590f6b7716bd..94abf1cdaedb5 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -2862,7 +2862,7 @@ function sp_type_rewrap(@nospecialize(T), mi::MethodInstance, isreturn::Bool) end function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, sstate::StatementState, sv::AbsIntState) - f = abstract_eval_value(interp, e.args[2], sstate, sv) + (f, exct) = abstract_eval_value(interp, e.args[2], sstate, sv) # rt = sp_type_rewrap(e.args[3], sv.linfo, true) atv = e.args[4]::SimpleVector at = Vector{Any}(undef, length(atv) + 1) @@ -2921,30 +2921,32 @@ function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, sv::AbsI # @assert false "Unexpected EXPR head in value position" merge_effects!(interp, sv, EFFECTS_UNKNOWN) end - return Any + return Pair{Any, Any}(Any, Any) end function abstract_eval_value(interp::AbstractInterpreter, @nospecialize(e), sstate::StatementState, sv::AbsIntState) if isa(e, Expr) return abstract_eval_value_expr(interp, e, sv) else - (;rt, effects) = abstract_eval_special_value(interp, e, sstate, sv) + (;rt, exct, effects) = abstract_eval_special_value(interp, e, sstate, sv) merge_effects!(interp, sv, effects) - return collect_limitations!(rt, sv) + return Pair{Any, Any}(collect_limitations!(rt, sv), exct) end end function collect_argtypes(interp::AbstractInterpreter, ea::Vector{Any}, sstate::StatementState, sv::AbsIntState) n = length(ea) argtypes = Vector{Any}(undef, n) + exct = Union{} @inbounds for i = 1:n - ai = abstract_eval_value(interp, ea[i], sstate, sv) + (ai, ei) = abstract_eval_value(interp, ea[i], sstate, sv) if ai === Bottom return nothing end argtypes[i] = ai + exct = Union{exct, ei} end - return argtypes + return Pair{Vector{Any}, Any}(argtypes, exct) end struct RTEffects @@ -2984,21 +2986,23 @@ function abstract_eval_call(interp::AbstractInterpreter, e::Expr, sstate::Statem if argtypes === nothing return Future(RTEffects(Bottom, Any, Effects())) end + (argtypes, aexct) = argtypes arginfo = ArgInfo(ea, argtypes) call = abstract_call(interp, arginfo, sstate, sv)::Future return Future{RTEffects}(call, interp, sv) do call, interp, sv (; rt, exct, effects, refinements) = call - return RTEffects(rt, exct, effects, refinements) + return RTEffects(rt, tmerge(typeinf_lattice(interp), exct, aexct), effects, refinements) end end - +const generic_new_exct = Union{ErrorException, TypeError} function abstract_eval_new(interp::AbstractInterpreter, e::Expr, sstate::StatementState, sv::AbsIntState) ๐•ƒแตข = typeinf_lattice(interp) - rt, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], sstate, sv), true) + (ea1, a1exct) = abstract_eval_value(interp, e.args[1], sstate, sv) + rt, isexact = instanceof_tfunc(ea1, true) ut = unwrap_unionall(rt) - exct = Union{ErrorException,TypeError} + nothrow = false if isa(ut, DataType) && !isabstracttype(ut) ismutable = ismutabletype(ut) fcount = datatype_fieldcount(ut) @@ -3018,12 +3022,15 @@ function abstract_eval_new(interp::AbstractInterpreter, e::Expr, sstate::Stateme end if isconcretedispatch(rt) nothrow = true + exct = Union{} @assert fcount !== nothing && fcount โ‰ฅ nargs "malformed :new expression" # syntactically enforced by the front-end ats = Vector{Any}(undef, nargs) local anyrefine = false local allconst = true for i = 1:nargs - at = widenslotwrapper(abstract_eval_value(interp, e.args[i+1], sstate, sv)) + (atn, aexct) = abstract_eval_value(interp, e.args[i+1], sstate, sv) + exct = Union{exct, aexct} + at = widenslotwrapper(atn) ft = fieldtype(rt, i) nothrow && (nothrow = โŠ‘(๐•ƒแตข, at, ft)) at = tmeet(๐•ƒแตข, at, ft) @@ -3039,6 +3046,7 @@ function abstract_eval_new(interp::AbstractInterpreter, e::Expr, sstate::Stateme end ats[i] = at end + nothrow || (exct = Union{exct, TypeError}) if fcount == nargs && consistent === ALWAYS_TRUE && allconst argvals = Vector{Any}(undef, nargs) for j in 1:nargs @@ -3054,13 +3062,13 @@ function abstract_eval_new(interp::AbstractInterpreter, e::Expr, sstate::Stateme end else rt = refine_partial_type(rt) - nothrow = false + exct = generic_new_exct end else consistent = ALWAYS_FALSE - nothrow = false + exct = generic_new_exct end - nothrow && (exct = Union{}) + exct = Union{exct, a1exct} effects = Effects(EFFECTS_TOTAL; consistent, nothrow) return RTEffects(rt, exct, effects) end @@ -3068,10 +3076,12 @@ end function abstract_eval_splatnew(interp::AbstractInterpreter, e::Expr, sstate::StatementState, sv::AbsIntState) ๐•ƒแตข = typeinf_lattice(interp) - rt, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], sstate, sv), true) + (a1, a1exct) = abstract_eval_value(interp, e.args[1], sstate, sv) + rt, isexact = instanceof_tfunc(a1, true) nothrow = false if length(e.args) == 2 && isconcretedispatch(rt) && !ismutabletype(rt) - at = abstract_eval_value(interp, e.args[2], sstate, sv) + (at, a2exct) = abstract_eval_value(interp, e.args[2], sstate, sv) + exct = Union{a1exct, a2exct} n = fieldcount(rt) if (isa(at, Const) && isa(at.val, Tuple) && n == length(at.val::Tuple) && (let t = rt, at = at @@ -3087,12 +3097,14 @@ function abstract_eval_splatnew(interp::AbstractInterpreter, e::Expr, sstate::St nothrow = isexact rt = PartialStruct(๐•ƒแตข, rt, at.fields::Vector{Any}) end + nothrow || (exct = Union{exct, generic_new_exct}) else rt = refine_partial_type(rt) + exct = Any end consistent = !ismutabletype(rt) ? ALWAYS_TRUE : CONSISTENT_IF_NOTRETURNED effects = Effects(EFFECTS_TOTAL; consistent, nothrow) - return RTEffects(rt, Any, effects) + return RTEffects(rt, exct, effects) end function abstract_eval_new_opaque_closure(interp::AbstractInterpreter, e::Expr, sstate::StatementState, @@ -3107,6 +3119,7 @@ function abstract_eval_new_opaque_closure(interp::AbstractInterpreter, e::Expr, rt = Bottom effects = EFFECTS_THROWS else + (argtypes, _) = argtypes mi = frame_instance(sv) rt = opaque_closure_tfunc(๐•ƒแตข, argtypes[1], argtypes[2], argtypes[3], argtypes[5], argtypes[6:end], mi) @@ -3134,7 +3147,7 @@ end function abstract_eval_copyast(interp::AbstractInterpreter, e::Expr, sstate::StatementState, sv::AbsIntState) effects = EFFECTS_UNKNOWN - rt = abstract_eval_value(interp, e.args[1], sstate, sv) + (rt, _) = abstract_eval_value(interp, e.args[1], sstate, sv) if rt isa Const && rt.val isa Expr # `copyast` makes copies of Exprs rt = Expr @@ -3190,7 +3203,7 @@ function abstract_eval_isdefined(interp::AbstractInterpreter, @nospecialize(sym) end function abstract_eval_throw_undef_if_not(interp::AbstractInterpreter, e::Expr, sstate::StatementState, sv::AbsIntState) - condt = abstract_eval_value(interp, e.args[2], sstate, sv) + (condt, argexct) = abstract_eval_value(interp, e.args[2], sstate, sv) condval = maybe_extract_const_bool(condt) rt = Nothing exct = UndefVarError @@ -3205,7 +3218,7 @@ function abstract_eval_throw_undef_if_not(interp::AbstractInterpreter, e::Expr, elseif !hasintersect(widenconst(condt), Bool) rt = Union{} end - return RTEffects(rt, exct, effects) + return RTEffects(rt, Union{exct, argexct}, effects) end function abstract_eval_the_exception(::AbstractInterpreter, sv::InferenceState) @@ -3275,8 +3288,8 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, ssta # N.B.: abstract_eval_value_expr can modify the global effects, but # we move out any arguments with effects during SSA construction later # and recompute the effects. - rt = abstract_eval_value_expr(interp, e, sv) - return RTEffects(rt, Any, EFFECTS_TOTAL) + (rt, exct) = abstract_eval_value_expr(interp, e, sv) + return RTEffects(rt, exct, EFFECTS_TOTAL) end # refine the result of instantiation of partially-known type `t` if some invariant can be assumed @@ -3296,12 +3309,14 @@ function abstract_eval_foreigncall(interp::AbstractInterpreter, e::Expr, sstate: mi = frame_instance(sv) t = sp_type_rewrap(e.args[2], mi, true) for i = 3:length(e.args) - if abstract_eval_value(interp, e.args[i], sstate, sv) === Bottom + (at, aexct) = abstract_eval_value(interp, e.args[i], sstate, sv) + if at === Bottom return RTEffects(Bottom, Any, EFFECTS_THROWS) end end effects = foreigncall_effects(e) do @nospecialize x - abstract_eval_value(interp, x, sstate, sv) + (at, aexct) = abstract_eval_value(interp, x, sstate, sv) + at end cconv = e.args[5] if isa(cconv, QuoteNode) && (v = cconv.value; isa(v, Tuple{Symbol, UInt16})) @@ -3367,6 +3382,10 @@ end function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode, IncrementalCompact}, retry_after_resolve::Bool=true) worlds = world_range(src) + abstract_eval_globalref_type(g, worlds, retry_after_resolve) +end + +function abstract_eval_globalref_type(g::GlobalRef, worlds::WorldRange, retry_after_resolve::Bool=true) partition = lookup_binding_partition(min_world(worlds), g) partition.max_world < max_world(worlds) && return Any while is_some_imported(binding_kind(partition)) @@ -3380,7 +3399,7 @@ function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode, # 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) - return abstract_eval_globalref_type(g, src, false) + return abstract_eval_globalref_type(g, worlds, false) end # return Union{} return Any @@ -3391,6 +3410,11 @@ function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode, return partition_restriction(partition) end +function is_global_nothrow_const_in_all_worlds(g::GlobalRef, worlds::WorldRange, retry_after_resolve::Bool=true) + # TODO: We may be fine if there's two different partitions with different constants + return isa(abstract_eval_globalref_type(g, worlds, retry_after_resolve), Const) +end + function abstract_eval_binding_partition!(interp::AbstractInterpreter, g::GlobalRef, sv::AbsIntState) force_binding_resolution!(g) partition = lookup_binding_partition(get_inference_world(interp), g) @@ -3821,7 +3845,8 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState, nextr elseif isa(stmt, GotoIfNot) condx = stmt.cond condslot = ssa_def_slot(condx, frame) - condt = abstract_eval_value(interp, condx, StatementState(currstate, currsaw_latestworld), frame) + condt, aexct = abstract_eval_value(interp, condx, StatementState(currstate, currsaw_latestworld), frame) + @assert aexct === Bottom # Guaranteed in lowering if condt === Bottom ssavaluetypes[currpc] = Bottom empty!(frame.pclimitations) @@ -3908,7 +3933,11 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState, nextr @goto fallthrough end elseif isa(stmt, ReturnNode) - rt = abstract_eval_value(interp, stmt.val, StatementState(currstate, currsaw_latestworld), frame) + rt, rexct = abstract_eval_value(interp, stmt.val, StatementState(currstate, currsaw_latestworld), frame) + if rexct !== Union{} + update_exc_bestguess!(interp, rexct, frame) + propagate_to_error_handler!(currstate, currsaw_latestworld, frame, ๐•ƒแตข) + end if update_bestguess!(interp, frame, currstate, rt) update_cycle_worklists!(frame) do caller::InferenceState, caller_pc::Int # no reason to revisit if that call-site doesn't affect the final result @@ -3921,7 +3950,8 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState, nextr ssavaluetypes[currpc] = Any add_curr_ssaflag!(frame, IR_FLAG_NOTHROW) if isdefined(stmt, :scope) - scopet = abstract_eval_value(interp, stmt.scope, StatementState(currstate, currsaw_latestworld), frame) + scopet, sexct = abstract_eval_value(interp, stmt.scope, StatementState(currstate, currsaw_latestworld), frame) + @assert sexct === Bottom # Guaranteed in lowering handler = gethandler(frame, currpc + 1)::TryCatchFrame @assert handler.scopet !== nothing if !โŠ‘(๐•ƒแตข, scopet, handler.scopet) @@ -4023,13 +4053,13 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState, nextr changes = StateUpdate(lhs::SlotNumber, VarState(rt, false)) end end - if !has_curr_ssaflag(frame, IR_FLAG_NOTHROW) - if exct !== Union{} - update_exc_bestguess!(interp, exct, frame) - # TODO: assert that these conditions match. For now, we assume the `nothrow` flag - # to be correct, but allow the exct to be an over-approximation. - end + if exct !== Union{} + update_exc_bestguess!(interp, exct, frame) propagate_to_error_handler!(currstate, currsaw_latestworld, frame, ๐•ƒแตข) + # TODO: assert that these conditions match. For now, we assume the `nothrow` flag + # to be correct, but allow the exct to be an over-approximation. + else + has_curr_ssaflag(frame, IR_FLAG_NOTHROW) end if rt === Bottom ssavaluetypes[currpc] = Bottom diff --git a/Compiler/src/optimize.jl b/Compiler/src/optimize.jl index 856e64e404388..668cfb0e78061 100644 --- a/Compiler/src/optimize.jl +++ b/Compiler/src/optimize.jl @@ -163,12 +163,14 @@ mutable struct OptimizationState{Interp<:AbstractInterpreter} unreachable::BitSet bb_vartables::Vector{Union{Nothing,VarTable}} insert_coverage::Bool + valid_worlds::WorldRange end function OptimizationState(sv::InferenceState, interp::AbstractInterpreter) inlining = InliningState(sv, interp) return OptimizationState(sv.linfo, sv.src, nothing, sv.stmt_info, sv.mod, sv.sptypes, sv.slottypes, inlining, sv.cfg, - sv.unreachable, sv.bb_vartables, sv.insert_coverage) + sv.unreachable, sv.bb_vartables, sv.insert_coverage, + sv.world.valid_worlds) end function OptimizationState(mi::MethodInstance, src::CodeInfo, interp::AbstractInterpreter) # prepare src for running optimization passes if it isn't already @@ -200,7 +202,8 @@ function OptimizationState(mi::MethodInstance, src::CodeInfo, interp::AbstractIn for slot = 1:nslots ]) end - return OptimizationState(mi, src, nothing, stmt_info, mod, sptypes, slottypes, inlining, cfg, unreachable, bb_vartables, false) + return OptimizationState(mi, src, nothing, stmt_info, mod, sptypes, slottypes, inlining, cfg, unreachable, bb_vartables, false, + WorldRange(get_inference_world(interp), get_inference_world(interp))) end function OptimizationState(mi::MethodInstance, interp::AbstractInterpreter) world = get_inference_world(interp) @@ -1175,31 +1178,52 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState) nstmts = length(code) ssachangemap = labelchangemap = blockchangemap = nothing prevloc = 0 + + function insert_statement_here!(@nospecialize(new_stmt), @nospecialize(new_typ); after=false) + insert_idx = after ? idx + 1 : idx + insert!(code, insert_idx, new_stmt) + splice!(codelocs, 3idx-2:3idx-3, (codelocs[3idx-2], codelocs[3idx-1], codelocs[3idx-0])) + insert!(ssavaluetypes, insert_idx, new_typ) + insert!(stmtinfo, insert_idx, NoCallInfo()) + insert!(ssaflags, insert_idx, IR_FLAG_NULL) + if ssachangemap === nothing + ssachangemap = fill(0, nstmts) + end + if labelchangemap === nothing + labelchangemap = fill(0, nstmts) + end + ssachangemap[after ? oldidx + 1 : oldidx] += 1 + if oldidx < length(labelchangemap) + labelchangemap[oldidx + 1] += 1 + end + if blockchangemap === nothing + blockchangemap = fill(0, length(sv.cfg.blocks)) + end + blockchangemap[block_for_inst(sv.cfg, oldidx)] += 1 + idx += 1 + end + while idx <= length(code) if sv.insert_coverage && changed_lineinfo(ci.debuginfo, oldidx, prevloc) # insert a side-effect instruction before the current instruction in the same basic block - insert!(code, idx, Expr(:code_coverage_effect)) - splice!(codelocs, 3idx-2:3idx-3, (codelocs[3idx-2], codelocs[3idx-1], codelocs[3idx-0])) - insert!(ssavaluetypes, idx, Nothing) - insert!(stmtinfo, idx, NoCallInfo()) - insert!(ssaflags, idx, IR_FLAG_NULL) - if ssachangemap === nothing - ssachangemap = fill(0, nstmts) - end - if labelchangemap === nothing - labelchangemap = fill(0, nstmts) - end - ssachangemap[oldidx] += 1 - if oldidx < length(labelchangemap) - labelchangemap[oldidx + 1] += 1 - end - if blockchangemap === nothing - blockchangemap = fill(0, length(sv.cfg.blocks)) - end - blockchangemap[block_for_inst(sv.cfg, oldidx)] += 1 - idx += 1 + insert_statement_here!(Expr(:code_coverage_effect), Nothing) prevloc = oldidx end + stmt = code[idx] + # Move out any GlobalRefs that may throw into statement position to satisfy IRCode invariants + updated_any = false + if !isa(stmt, GlobalRef) && !isexpr(stmt, :isdefined) + urs = userefs(stmt) + for ur in urs + arg = ur[] + if isa(arg, GlobalRef) && !is_global_nothrow_const_in_all_worlds(arg, sv.valid_worlds) + ur[] = NewSSAValue(idx) + insert_statement_here!(arg, abstract_eval_globalref_type(arg, sv.valid_worlds)) + updated_any = true + end + end + updated_any && (code[idx] = urs[]) + end if ssavaluetypes[idx] === Union{} && !(oldidx in sv.unreachable) && !isa(code[idx], PhiNode) # We should have converted any must-throw terminators to an equivalent w/o control-flow edges @assert !isterminator(code[idx]) @@ -1233,26 +1257,7 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState) ssaflags[block_end] = IR_FLAG_NOTHROW idx += block_end - idx else - insert!(code, idx + 1, ReturnNode()) - splice!(codelocs, 3idx-2:3idx-3, (codelocs[3idx-2], codelocs[3idx-1], codelocs[3idx-0])) - insert!(ssavaluetypes, idx + 1, Union{}) - insert!(stmtinfo, idx + 1, NoCallInfo()) - insert!(ssaflags, idx + 1, IR_FLAG_NOTHROW) - if ssachangemap === nothing - ssachangemap = fill(0, nstmts) - end - if labelchangemap === nothing - labelchangemap = sv.insert_coverage ? fill(0, nstmts) : ssachangemap - end - if oldidx < length(ssachangemap) - ssachangemap[oldidx + 1] += 1 - sv.insert_coverage && (labelchangemap[oldidx + 1] += 1) - end - if blockchangemap === nothing - blockchangemap = fill(0, length(sv.cfg.blocks)) - end - blockchangemap[block] += 1 - idx += 1 + insert_statement_here!(ReturnNode(), Union{}; after=true) end oldidx = last(sv.cfg.blocks[block].stmts) end @@ -1500,6 +1505,8 @@ function renumber_ir_elements!(body::Vector{Any}, ssachangemap::Vector{Int}, lab cond = el.cond if isa(cond, SSAValue) cond = SSAValue(cond.id + ssachangemap[cond.id]) + elseif isa(cond, NewSSAValue) + cond = SSAValue(cond.id) end was_deleted = labelchangemap[el.dest] == typemin(Int) body[i] = was_deleted ? cond : GotoIfNot(cond, el.dest + labelchangemap[el.dest]) @@ -1508,6 +1515,8 @@ function renumber_ir_elements!(body::Vector{Any}, ssachangemap::Vector{Int}, lab val = el.val if isa(val, SSAValue) body[i] = ReturnNode(SSAValue(val.id + ssachangemap[val.id])) + elseif isa(val, NewSSAValue) + body[i] = ReturnNode(SSAValue(val.id)) end end elseif isa(el, SSAValue) @@ -1527,6 +1536,7 @@ function renumber_ir_elements!(body::Vector{Any}, ssachangemap::Vector{Int}, lab if isa(val, SSAValue) values[i] = SSAValue(val.id + ssachangemap[val.id]) end + @assert !isa(val, NewSSAValue) i += 1 end end @@ -1538,8 +1548,14 @@ function renumber_ir_elements!(body::Vector{Any}, ssachangemap::Vector{Int}, lab @assert !isdefined(el, :scope) body[i] = nothing else - if isdefined(el, :scope) && isa(el.scope, SSAValue) - body[i] = EnterNode(tgt + labelchangemap[tgt], SSAValue(el.scope.id + ssachangemap[el.scope.id])) + if isdefined(el, :scope) + if isa(el.scope, SSAValue) + body[i] = EnterNode(tgt + labelchangemap[tgt], SSAValue(el.scope.id + ssachangemap[el.scope.id])) + elseif isa(el.scope, NewSSAValue) + body[i] = EnterNode(tgt + labelchangemap[tgt], SSAValue(el.scope.id)) + else + body[i] = EnterNode(el, tgt + labelchangemap[tgt]) + end else body[i] = EnterNode(el, tgt + labelchangemap[tgt]) end @@ -1555,10 +1571,13 @@ function renumber_ir_elements!(body::Vector{Any}, ssachangemap::Vector{Int}, lab el = args[i] if isa(el, SSAValue) args[i] = SSAValue(el.id + ssachangemap[el.id]) + elseif isa(el, NewSSAValue) + args[i] = SSAValue(el.id) end end end end + #@assert !isa(el, NewSSAValue) end end diff --git a/Compiler/src/ssair/irinterp.jl b/Compiler/src/ssair/irinterp.jl index a4969e81828cc..935cbd7a53ab9 100644 --- a/Compiler/src/ssair/irinterp.jl +++ b/Compiler/src/ssair/irinterp.jl @@ -44,6 +44,7 @@ function abstract_eval_invoke_inst(interp::AbstractInterpreter, inst::Instructio end argtypes = collect_argtypes(interp, stmt.args[2:end], StatementState(nothing, false), irsv) argtypes === nothing && return Pair{Any,Tuple{Bool,Bool}}(Bottom, (false, false)) + (argtypes, _) = argtypes return concrete_eval_invoke(interp, code, argtypes, irsv) end @@ -308,7 +309,8 @@ populate_def_use_map!(tpdum::TwoPhaseDefUseMap, ir::IRCode) = function is_all_const_call(@nospecialize(stmt), interp::AbstractInterpreter, irsv::IRInterpretationState) isexpr(stmt, :call) || return false @inbounds for i = 2:length(stmt.args) - argtype = abstract_eval_value(interp, stmt.args[i], StatementState(nothing, false), irsv) + # Discard exct - IRCode semantics require this to be Bottom + (argtype, _) = abstract_eval_value(interp, stmt.args[i], StatementState(nothing, false), irsv) is_const_argtype(argtype) || return false end return true diff --git a/Compiler/src/ssair/verify.jl b/Compiler/src/ssair/verify.jl index 072a564a31f78..7b043a79377da 100644 --- a/Compiler/src/ssair/verify.jl +++ b/Compiler/src/ssair/verify.jl @@ -61,7 +61,7 @@ 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) + if !is_global_nothrow_const_in_all_worlds(op, ir.valid_worlds) @verify_error "Unbound GlobalRef not allowed in value position" raise_error() end diff --git a/src/ast.c b/src/ast.c index 474c0661f5230..959bac26c2c15 100644 --- a/src/ast.c +++ b/src/ast.c @@ -181,42 +181,6 @@ static value_t fl_defined_julia_global(fl_context_t *fl_ctx, value_t *args, uint return (bpart != NULL && decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) == BINDING_KIND_GLOBAL) ? fl_ctx->T : fl_ctx->F; } -static value_t fl_nothrow_julia_global(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) -{ - // tells whether a var is defined, in the sense that accessing it is nothrow - // can take either a symbol or a module and a symbol - jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx); - jl_module_t *mod = ctx->module; - jl_sym_t *var = NULL; - if (nargs == 1) { - (void)tosymbol(fl_ctx, args[0], "nothrow-julia-global"); - var = scmsym_to_julia(fl_ctx, args[0]); - } - else { - argcount(fl_ctx, "nothrow-julia-global", nargs, 2); - value_t argmod = args[0]; - if (iscvalue(argmod) && cv_class((cvalue_t*)ptr(argmod)) == jl_ast_ctx(fl_ctx)->jvtype) { - mod = *(jl_module_t**)cv_data((cvalue_t*)ptr(argmod)); - JL_GC_PROMISE_ROOTED(mod); - } else { - if (!iscons(argmod) || !issymbol(car_(argmod)) || scmsym_to_julia(fl_ctx, car_(argmod)) != jl_thismodule_sym) { - lerrorf(fl_ctx, fl_ctx->ArgError, "nothrow-julia-global: Unknown globalref module kind"); - } - } - (void)tosymbol(fl_ctx, args[1], "nothrow-julia-global"); - var = scmsym_to_julia(fl_ctx, args[1]); - } - jl_binding_t *b = jl_get_module_binding(mod, var, 0); - jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - jl_ptr_kind_union_t pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); - if (!bpart) - return fl_ctx->F; - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) - return fl_ctx->F; - return (jl_bkind_is_some_constant(decode_restriction_kind(pku)) ? - decode_restriction_value(pku) : jl_atomic_load_relaxed(&b->value)) != NULL ? fl_ctx->T : fl_ctx->F; -} - // Used to generate a unique suffix for a given symbol (e.g. variable or type name) // first argument contains a stack of method definitions seen so far by `closure-convert` in flisp. // if the top of the stack is non-NIL, we use it to augment the suffix so that it becomes @@ -288,7 +252,6 @@ static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, jl_module_t *m static const builtinspec_t julia_flisp_ast_ext[] = { { "defined-julia-global", fl_defined_julia_global }, // TODO: can we kill this safepoint - { "nothrow-julia-global", fl_nothrow_julia_global }, { "current-julia-module-counter", fl_module_unique_name }, { "julia-scalar?", fl_julia_scalar }, { NULL, NULL } diff --git a/src/jlfrontend.scm b/src/jlfrontend.scm index 3d46940d6fcbb..9c69da199c0cd 100644 --- a/src/jlfrontend.scm +++ b/src/jlfrontend.scm @@ -31,7 +31,6 @@ ;; this is overwritten when we run in actual julia (define (defined-julia-global v) #f) -(define (nothrow-julia-global v) #f) ;; parser entry points diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index c7ca5d553bb31..d5ac8cca0861e 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -4356,7 +4356,7 @@ f(x) = yt(x) (define (valid-ir-argument? e) (or (simple-atom? e) - (and (globalref? e) (nothrow-julia-global (cadr e) (caddr e))) + (globalref? e) (and (pair? e) (memq (car e) '(quote inert top core slot static_parameter))))) @@ -4534,6 +4534,8 @@ f(x) = yt(x) (or (memq aval (lam:args lam)) (let ((vi (get vinfo-table aval #f))) (and vi (vinfo:never-undef vi))))))) + (define (valid-cond-argument? aval) + (and (not (globalref? aval)) (valid-body-ir-argument? aval))) (define (single-assign-var? aval) (and (symbol? aval) ; Arguments are always sa (or (memq aval (lam:args lam)) @@ -4577,7 +4579,7 @@ f(x) = yt(x) (let ((cnd (or (compile ex break-labels #t #f) ;; TODO: condition exprs that don't yield a value? '(null)))) - (if (valid-body-ir-argument? cnd) cnd + (if (valid-cond-argument? cnd) cnd (let ((tmp (make-ssavalue))) (emit `(= ,tmp ,cnd)) tmp)))) diff --git a/test/core.jl b/test/core.jl index 39d02d5d567c9..52900fec687cb 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7461,23 +7461,22 @@ let code = code_lowered(FieldConvert)[1].code field_type2_ssa, field_type4_ssa, field_type5_ssa, slot_read_1, slot_read_2, slot_read_3, slot_read_4, new_ssa - @test code[(fc_global_ssa = 1;)] == GlobalRef(@__MODULE__, :FieldConvert) - @test code[(sp1_ssa = 2;)] == Expr(:static_parameter, 1) - @test code[(apply_type_ssa = 3;)] == Expr(:call, GlobalRef(Core, :apply_type), Core.SSAValue(fc_global_ssa), GlobalRef(@__MODULE__, :FieldTypeA), Core.SSAValue(sp1_ssa)) - @test code[(field_type_ssa = 4;)] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(apply_type_ssa), 1) - @test code[10] == Expr(:(=), Core.SlotNumber(10), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(field_type_ssa), Core.SlotNumber(10))) - @test code[(slot_read_1 = 11;)] == Core.SlotNumber(10) - @test code[(field_type2_ssa = 12;)] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(apply_type_ssa), 2) - @test code[18] == Expr(:(=), Core.SlotNumber(9), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(field_type2_ssa), Core.SlotNumber(9))) - @test code[(slot_read_2 = 19;)] == Core.SlotNumber(9) - @test code[(field_type4_ssa = 20;)] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(apply_type_ssa), 4) - @test code[26] == Expr(:(=), Core.SlotNumber(8), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(field_type4_ssa), Core.SlotNumber(8))) - @test code[(slot_read_3 = 27;)] == Core.SlotNumber(8) - @test code[(field_type5_ssa = 28;)] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(apply_type_ssa), 5) - @test code[34] == Expr(:(=), Core.SlotNumber(7), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(field_type5_ssa), Core.SlotNumber(7))) - @test code[(slot_read_4 = 35;)] == Core.SlotNumber(7) - @test code[(new_ssa = 36;)] == Expr(:new, Core.SSAValue(apply_type_ssa), Core.SSAValue(slot_read_1), Core.SSAValue(slot_read_2), Core.SlotNumber(4), Core.SSAValue(slot_read_3), Core.SSAValue(slot_read_4)) - @test code[37] == Core.ReturnNode(Core.SSAValue(new_ssa)) + @test code[(sp1_ssa = 1;)] == Expr(:static_parameter, 1) + @test code[(apply_type_ssa = 2;)] == Expr(:call, GlobalRef(Core, :apply_type), GlobalRef(@__MODULE__, :FieldConvert), GlobalRef(@__MODULE__, :FieldTypeA), Core.SSAValue(sp1_ssa)) + @test code[(field_type_ssa = 3;)] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(apply_type_ssa), 1) + @test code[9] == Expr(:(=), Core.SlotNumber(10), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(field_type_ssa), Core.SlotNumber(10))) + @test code[(slot_read_1 = 10;)] == Core.SlotNumber(10) + @test code[(field_type2_ssa = 11;)] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(apply_type_ssa), 2) + @test code[17] == Expr(:(=), Core.SlotNumber(9), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(field_type2_ssa), Core.SlotNumber(9))) + @test code[(slot_read_2 = 18;)] == Core.SlotNumber(9) + @test code[(field_type4_ssa = 19;)] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(apply_type_ssa), 4) + @test code[25] == Expr(:(=), Core.SlotNumber(8), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(field_type4_ssa), Core.SlotNumber(8))) + @test code[(slot_read_3 = 26;)] == Core.SlotNumber(8) + @test code[(field_type5_ssa = 27;)] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(apply_type_ssa), 5) + @test code[33] == Expr(:(=), Core.SlotNumber(7), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(field_type5_ssa), Core.SlotNumber(7))) + @test code[(slot_read_4 = 34;)] == Core.SlotNumber(7) + @test code[(new_ssa = 35;)] == Expr(:new, Core.SSAValue(apply_type_ssa), Core.SSAValue(slot_read_1), Core.SSAValue(slot_read_2), Core.SlotNumber(4), Core.SSAValue(slot_read_3), Core.SSAValue(slot_read_4)) + @test code[36] == Core.ReturnNode(Core.SSAValue(new_ssa)) end # Issue #32820