diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 82aca46f3e7ed..e387352cf8018 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2230,31 +2230,31 @@ end function abstract_eval_special_value(interp::AbstractInterpreter, @nospecialize(e), vtypes::Union{VarTable,Nothing}, sv::AbsIntState) if isa(e, QuoteNode) - return Const(e.value) + return RTEffects(Const(e.value), EFFECTS_TOTAL) elseif isa(e, SSAValue) - return abstract_eval_ssavalue(e, sv) + return RTEffects(abstract_eval_ssavalue(e, sv), EFFECTS_TOTAL) elseif isa(e, SlotNumber) + effects = EFFECTS_THROWS if vtypes !== nothing vtyp = vtypes[slot_id(e)] - if vtyp.undef - merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; nothrow=false)) + if !vtyp.undef + effects = EFFECTS_TOTAL end - return vtyp.typ + return RTEffects(vtyp.typ, effects) end - merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; nothrow=false)) - return Any + return RTEffects(Any, effects) elseif isa(e, Argument) if vtypes !== nothing - return vtypes[slot_id(e)].typ + return RTEffects(vtypes[slot_id(e)].typ, EFFECTS_TOTAL) else @assert isa(sv, IRInterpretationState) - return sv.ir.argtypes[e.n] # TODO frame_argtypes(sv)[e.n] and remove the assertion + return RTEffects(sv.ir.argtypes[e.n], EFFECTS_TOTAL) # TODO frame_argtypes(sv)[e.n] and remove the assertion end elseif isa(e, GlobalRef) return abstract_eval_globalref(interp, e, sv) end - return Const(e) + return RTEffects(Const(e), EFFECTS_TOTAL) end function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable,Nothing}, sv::AbsIntState) @@ -2288,8 +2288,9 @@ function abstract_eval_value(interp::AbstractInterpreter, @nospecialize(e), vtyp if isa(e, Expr) return abstract_eval_value_expr(interp, e, vtypes, sv) else - typ = abstract_eval_special_value(interp, e, vtypes, sv) - return collect_limitations!(typ, sv) + (;rt, effects) = abstract_eval_special_value(interp, e, vtypes, sv) + merge_effects!(interp, sv, effects) + return collect_limitations!(rt, sv) end end @@ -2615,7 +2616,9 @@ function abstract_eval_phi(interp::AbstractInterpreter, phi::PhiNode, vtypes::Un for i in 1:length(phi.values) isassigned(phi.values, i) || continue val = phi.values[i] - rt = tmerge(typeinf_lattice(interp), rt, abstract_eval_special_value(interp, val, vtypes, sv)) + # N.B.: Phi arguments are restricted to not have effects, so we can drop + # them here safely. + rt = tmerge(typeinf_lattice(interp), rt, abstract_eval_special_value(interp, val, vtypes, sv).rt) end return rt end @@ -2631,16 +2634,28 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), add_curr_ssaflag!(sv, IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW) return abstract_eval_phi(interp, e, vtypes, sv) end - return abstract_eval_special_value(interp, e, vtypes, sv) - end - (; rt, effects) = abstract_eval_statement_expr(interp, e, vtypes, sv) - if effects.noub === NOUB_IF_NOINBOUNDS - if !iszero(get_curr_ssaflag(sv) & IR_FLAG_INBOUNDS) - effects = Effects(effects; noub=ALWAYS_FALSE) - elseif !propagate_inbounds(sv) - # The callee read our inbounds flag, but unless we propagate inbounds, - # we ourselves don't read our parent's inbounds. - effects = Effects(effects; noub=ALWAYS_TRUE) + (; rt, effects) = abstract_eval_special_value(interp, e, vtypes, sv) + else + (; rt, effects) = abstract_eval_statement_expr(interp, e, vtypes, sv) + if effects.noub === NOUB_IF_NOINBOUNDS + if !iszero(get_curr_ssaflag(sv) & IR_FLAG_INBOUNDS) + effects = Effects(effects; noub=ALWAYS_FALSE) + elseif !propagate_inbounds(sv) + # The callee read our inbounds flag, but unless we propagate inbounds, + # we ourselves don't read our parent's inbounds. + effects = Effects(effects; noub=ALWAYS_TRUE) + end + end + e = e::Expr + @assert !isa(rt, TypeVar) "unhandled TypeVar" + rt = maybe_singleton_const(rt) + if !isempty(sv.pclimitations) + if rt isa Const || rt === Union{} + empty!(sv.pclimitations) + else + rt = LimitedAccuracy(rt, sv.pclimitations) + sv.pclimitations = IdSet{InferenceState}() + end end end # N.B.: This only applies to the effects of the statement itself. @@ -2648,17 +2663,7 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), # but these will be recomputed during SSA construction later. set_curr_ssaflag!(sv, flags_for_effects(effects), IR_FLAGS_EFFECTS) merge_effects!(interp, sv, effects) - e = e::Expr - @assert !isa(rt, TypeVar) "unhandled TypeVar" - rt = maybe_singleton_const(rt) - if !isempty(sv.pclimitations) - if rt isa Const || rt === Union{} - empty!(sv.pclimitations) - else - rt = LimitedAccuracy(rt, sv.pclimitations) - sv.pclimitations = IdSet{InferenceState}() - end - end + return rt end @@ -2666,7 +2671,7 @@ function isdefined_globalref(g::GlobalRef) return ccall(:jl_globalref_boundp, Cint, (Any,), g) != 0 end -function abstract_eval_globalref(g::GlobalRef) +function abstract_eval_globalref_type(g::GlobalRef) if isdefined_globalref(g) && isconst(g) return Const(ccall(:jl_get_globalref_value, Any, (Any,), g)) end @@ -2674,10 +2679,10 @@ function abstract_eval_globalref(g::GlobalRef) ty === nothing && return Any return ty end -abstract_eval_global(M::Module, s::Symbol) = abstract_eval_globalref(GlobalRef(M, s)) +abstract_eval_global(M::Module, s::Symbol) = abstract_eval_globalref_type(GlobalRef(M, s)) function abstract_eval_globalref(interp::AbstractInterpreter, g::GlobalRef, sv::AbsIntState) - rt = abstract_eval_globalref(g) + rt = abstract_eval_globalref_type(g) consistent = inaccessiblememonly = ALWAYS_FALSE nothrow = false if isa(rt, Const) @@ -2692,8 +2697,7 @@ function abstract_eval_globalref(interp::AbstractInterpreter, g::GlobalRef, sv:: consistent = inaccessiblememonly = ALWAYS_TRUE rt = Union{} end - merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; consistent, nothrow, inaccessiblememonly)) - return rt + return RTEffects(rt, Effects(EFFECTS_TOTAL; consistent, nothrow, inaccessiblememonly)) end function handle_global_assignment!(interp::AbstractInterpreter, frame::InferenceState, lhs::GlobalRef, @nospecialize(newty)) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 305c9b5c03b10..9dff1281e83c5 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -396,7 +396,7 @@ function argextype( elseif isa(x, QuoteNode) return Const(x.value) elseif isa(x, GlobalRef) - return abstract_eval_globalref(x) + return abstract_eval_globalref_type(x) elseif isa(x, PhiNode) return Any elseif isa(x, PiNode) diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index 1f682d9168413..f3253526d25e9 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -207,7 +207,7 @@ function typ_for_val(@nospecialize(x), ci::CodeInfo, ir::IRCode, idx::Int, slott end return (ci.ssavaluetypes::Vector{Any})[idx] end - isa(x, GlobalRef) && return abstract_eval_globalref(x) + isa(x, GlobalRef) && return abstract_eval_globalref_type(x) isa(x, SSAValue) && return (ci.ssavaluetypes::Vector{Any})[x.id] isa(x, Argument) && return slottypes[x.n] isa(x, NewSSAValue) && return types(ir)[new_to_regular(x, length(ir.stmts))] diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 8f980bb681142..f46f9f19ee629 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -450,7 +450,7 @@ function is_throw_call(e::Expr) if e.head === :call f = e.args[1] if isa(f, GlobalRef) - ff = abstract_eval_globalref(f) + ff = abstract_eval_globalref_type(f) if isa(ff, Const) && ff.val === Core.throw return true end diff --git a/src/cgutils.cpp b/src/cgutils.cpp index e0e48ddf81d53..f158c182744e2 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1250,7 +1250,7 @@ static Value *emit_sizeof(jl_codectx_t &ctx, const jl_cgval_t &p) BasicBlock *dynloadBB = BasicBlock::Create(ctx.builder.getContext(), "dyn_sizeof", ctx.f); BasicBlock *postBB = BasicBlock::Create(ctx.builder.getContext(), "post_sizeof", ctx.f); Value *isboxed = ctx.builder.CreateICmpNE( - ctx.builder.CreateAnd(p.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)), + ctx.builder.CreateAnd(p.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)), ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)); ctx.builder.CreateCondBr(isboxed, dynloadBB, postBB); ctx.builder.SetInsertPoint(dynloadBB); @@ -1406,6 +1406,9 @@ static void null_pointer_check(jl_codectx_t &ctx, Value *v, Value **nullcheck = template static Value *emit_guarded_test(jl_codectx_t &ctx, Value *ifnot, Value *defval, Func &&func) { + if (!ifnot) { + return func(); + } if (auto Cond = dyn_cast(ifnot)) { if (Cond->isZero()) return defval; @@ -1544,21 +1547,25 @@ static bool can_optimize_isa_union(jl_uniontype_t *type) } // a simple case of emit_isa that is obvious not to include a safe-point -static Value *emit_exactly_isa(jl_codectx_t &ctx, const jl_cgval_t &arg, jl_datatype_t *dt) +static Value *emit_exactly_isa(jl_codectx_t &ctx, const jl_cgval_t &arg, jl_datatype_t *dt, bool could_be_null=false) { assert(jl_is_concrete_type((jl_value_t*)dt)); if (arg.TIndex) { unsigned tindex = get_box_tindex(dt, arg.typ); if (tindex > 0) { // optimize more when we know that this is a split union-type where tindex = 0 is invalid - Value *xtindex = ctx.builder.CreateAnd(arg.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x7f)); + Value *xtindex = ctx.builder.CreateAnd(arg.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), ~UNION_BOX_MARKER)); auto isa = ctx.builder.CreateICmpEQ(xtindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), tindex)); setName(ctx.emission_context, isa, "exactly_isa"); return isa; } else if (arg.Vboxed) { - // test for (arg.TIndex == 0x80 && typeof(arg.V) == type) - Value *isboxed = ctx.builder.CreateICmpEQ(arg.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)); + // test for (arg.TIndex == UNION_BOX_MARKER && typeof(arg.V) == type) + Value *isboxed = ctx.builder.CreateICmpEQ(arg.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)); + if (could_be_null) { + isboxed = ctx.builder.CreateAnd(isboxed, + ctx.builder.CreateNot(null_pointer_cmp(ctx, arg.Vboxed))); + } setName(ctx.emission_context, isboxed, "isboxed"); BasicBlock *currBB = ctx.builder.GetInsertBlock(); BasicBlock *isaBB = BasicBlock::Create(ctx.builder.getContext(), "isa", ctx.f); @@ -1579,9 +1586,16 @@ static Value *emit_exactly_isa(jl_codectx_t &ctx, const jl_cgval_t &arg, jl_data return ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0); } } - auto isa = ctx.builder.CreateICmpEQ(emit_typeof(ctx, arg, false, true), emit_tagfrom(ctx, dt)); - setName(ctx.emission_context, isa, "exactly_isa"); - return isa; + Value *isnull = NULL; + if (could_be_null && arg.isboxed) { + isnull = null_pointer_cmp(ctx, arg.Vboxed); + } + Constant *Vfalse = ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0); + return emit_guarded_test(ctx, isnull, Vfalse, [&]{ + auto isa = ctx.builder.CreateICmpEQ(emit_typeof(ctx, arg, false, true), emit_tagfrom(ctx, dt)); + setName(ctx.emission_context, isa, "exactly_isa"); + return isa; + }); } static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, @@ -3152,7 +3166,7 @@ static AllocaInst *try_emit_union_alloca(jl_codectx_t &ctx, jl_uniontype_t *ut, * returning `Constant::getNullValue(ctx.types().T_pjlvalue)` in one of the skipped cases. If `skip` is not empty, * skip[0] (corresponding to unknown boxed) must always be set. In that * case, the calling code must separately deal with the case where - * `vinfo` is already an unknown boxed union (union tag 0x80). + * `vinfo` is already an unknown boxed union (union tag UNION_BOX_MARKER). */ // Returns ctx.types().T_prjlvalue static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallBitVector &skip) diff --git a/src/codegen.cpp b/src/codegen.cpp index 2365d0af334c5..9c4805398bb91 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1582,15 +1582,25 @@ struct jl_aliasinfo_t { }; // metadata tracking for a llvm Value* during codegen +const uint8_t UNION_BOX_MARKER = 0x80; struct jl_cgval_t { Value *V; // may be of type T* or T, or set to NULL if ghost (or if the value has not been initialized yet, for a variable definition) // For unions, we may need to keep a reference to the boxed part individually. // If this is non-NULL, then, at runtime, we satisfy the invariant that (for the corresponding - // runtime values) if `(TIndex | 0x80) != 0`, then `Vboxed == V` (by value). + // runtime values) if `(TIndex | UNION_BOX_MARKER) != 0`, then `Vboxed == V` (by value). // For convenience, we also set this value of isboxed values, in which case // it is equal (at compile time) to V. - // If this is non-NULL, it is always of type `T_prjlvalue` + + // If this is non-NULL (at compile time), it is always of type `T_prjlvalue`. + // N.B.: In general we expect this to always be a dereferenceable pointer at runtime. + // However, there are situations where this value may be a runtime NULL + // (PhiNodes with undef predecessors or PhiC with undef UpsilonNode). + // The middle-end arranges appropriate error checks before any use + // of this value that may read a non-dereferenceable Vboxed, with two + // exceptions: PhiNode and UpsilonNode arguments which need special + // handling to account for the possibility that this may be NULL. Value *Vboxed; + Value *TIndex; // if `V` is an unboxed (tagged) Union described by `typ`, this gives the DataType index (1-based, small int) as an i8 jl_value_t *constant; // constant value (rooted in linfo.def.roots) jl_value_t *typ; // the original type of V, never NULL @@ -2247,7 +2257,7 @@ static void CreateConditionalAbort(IRBuilder<> &irbuilder, Value *test) static jl_cgval_t convert_julia_type_union(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ, Value **skip) { // previous value was a split union, compute new index, or box - Value *new_tindex = ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80); + Value *new_tindex = ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER); SmallBitVector skip_box(1, true); Value *tindex = ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x7f)); if (jl_is_uniontype(typ)) { @@ -2290,14 +2300,14 @@ static jl_cgval_t convert_julia_type_union(jl_codectx_t &ctx, const jl_cgval_t & // some of the values are still unboxed if (!isa(new_tindex)) { Value *wasboxed = NULL; - // If the old value was boxed and unknown (type tag 0x80), + // If the old value was boxed and unknown (type tag UNION_BOX_MARKER), // it is possible that the tag was actually one of the types // that are now explicitly represented. To find out, we need // to compare typeof(v.Vboxed) (i.e. the type of the unknown // value) against all the types that are now explicitly // selected and select the appropriate one as our new tindex. if (v.Vboxed) { - wasboxed = ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)); + wasboxed = ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)); new_tindex = ctx.builder.CreateOr(wasboxed, new_tindex); wasboxed = ctx.builder.CreateICmpNE(wasboxed, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)); setName(ctx.emission_context, wasboxed, "wasboxed"); @@ -2319,10 +2329,10 @@ static jl_cgval_t convert_julia_type_union(jl_codectx_t &ctx, const jl_cgval_t & }; // If we don't find a match. The type remains unknown - // (0x80). We could use `v.Tindex`, here, since we know - // it has to be 0x80, but it seems likely the backend + // (UNION_BOX_MARKER). We could use `v.Tindex`, here, since we know + // it has to be UNION_BOX_MARKER, but it seems likely the backend // will like the explicit constant better. - Value *union_box_tindex = ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80); + Value *union_box_tindex = ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER); unsigned counter = 0; for_each_uniontype_small( // for each new union-split value @@ -2332,7 +2342,7 @@ static jl_cgval_t convert_julia_type_union(jl_codectx_t &ctx, const jl_cgval_t & // didn't handle this item before, select its new union index maybe_setup_union_isa(); Value *cmp = ctx.builder.CreateICmpEQ(emit_tagfrom(ctx, jt), union_box_dt); - union_box_tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80 | idx), union_box_tindex); + union_box_tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER | idx), union_box_tindex); } }, typ, @@ -2342,7 +2352,7 @@ static jl_cgval_t convert_julia_type_union(jl_codectx_t &ctx, const jl_cgval_t & BasicBlock *postBB = BasicBlock::Create(ctx.builder.getContext(), "post_union_isa", ctx.f); ctx.builder.CreateBr(postBB); ctx.builder.SetInsertPoint(currBB); - Value *wasunknown = ctx.builder.CreateICmpEQ(v.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)); + Value *wasunknown = ctx.builder.CreateICmpEQ(v.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)); ctx.builder.CreateCondBr(wasunknown, union_isaBB, postBB); ctx.builder.SetInsertPoint(postBB); PHINode *tindex_phi = ctx.builder.CreatePHI(getInt8Ty(ctx.builder.getContext()), 2); @@ -2354,14 +2364,14 @@ static jl_cgval_t convert_julia_type_union(jl_codectx_t &ctx, const jl_cgval_t & } if (!skip_box.all()) { // some values weren't unboxed in the new union - // box them now (tindex above already selected 0x80 = box for them) + // box them now (tindex above already selected UNION_BOX_MARKER = box for them) Value *boxv = box_union(ctx, v, skip_box); if (v.Vboxed) { // If the value is boxed both before and after, we don't need // to touch it at all. Otherwise we're either transitioning // unboxed->boxed, or leaving an unboxed value in place. Value *isboxed = ctx.builder.CreateICmpNE( - ctx.builder.CreateAnd(new_tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)), + ctx.builder.CreateAnd(new_tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)), ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)); boxv = ctx.builder.CreateSelect( ctx.builder.CreateAnd(wasboxed, isboxed), v.Vboxed, boxv); @@ -2395,7 +2405,7 @@ static jl_cgval_t convert_julia_type_union(jl_codectx_t &ctx, const jl_cgval_t & } // given a value marked with type `v.typ`, compute the mapping and/or boxing to return a value of type `typ` -// TODO: should this set TIndex when trivial (such as 0x80 or concrete types) ? +// TODO: should this set TIndex when trivial (such as UNION_BOX_MARKER or concrete types) ? static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ, Value **skip) { if (typ == (jl_value_t*)jl_typeofbottom_type) @@ -2407,29 +2417,28 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ return ghostValue(ctx, typ); Value *new_tindex = NULL; if (jl_is_concrete_type(typ)) { - if (v.TIndex && !jl_is_pointerfree(typ)) { - // discovered that this union-split type must actually be isboxed - if (v.Vboxed) { - return jl_cgval_t(v.Vboxed, true, typ, NULL, best_tbaa(ctx.tbaa(), typ)); - } - else { - // type mismatch: there weren't any boxed values in the union - if (skip) - *skip = ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 1); - else - CreateTrap(ctx.builder); - return jl_cgval_t(); - } - } if (jl_is_concrete_type(v.typ)) { - if (jl_is_concrete_type(typ)) { - // type mismatch: changing from one leaftype to another - if (skip) - *skip = ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 1); - else - CreateTrap(ctx.builder); - return jl_cgval_t(); + // type mismatch: changing from one leaftype to another + if (skip) + *skip = ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 1); + else + CreateTrap(ctx.builder); + return jl_cgval_t(); + } + bool mustbox_union = v.TIndex && !jl_is_pointerfree(typ); + if (v.Vboxed && (v.isboxed || mustbox_union)) { + if (skip) { + *skip = ctx.builder.CreateNot(emit_exactly_isa(ctx, v, (jl_datatype_t*)typ, true)); } + return jl_cgval_t(v.Vboxed, true, typ, NULL, best_tbaa(ctx.tbaa(), typ)); + } + if (mustbox_union) { + // type mismatch: there weren't any boxed values in the union + if (skip) + *skip = ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 1); + else + CreateTrap(ctx.builder); + return jl_cgval_t(); } } else { @@ -4471,7 +4480,7 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos Value *tindex = ctx.builder.CreateExtractValue(call, 1); Value *derived = ctx.builder.CreateSelect( ctx.builder.CreateICmpEQ( - ctx.builder.CreateAnd(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)), + ctx.builder.CreateAnd(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)), ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)), decay_derived(ctx, ctx.builder.CreateBitCast(argvals[0], ctx.types().T_pjlvalue)), decay_derived(ctx, box) @@ -4952,10 +4961,10 @@ static jl_cgval_t emit_isdefined(jl_codectx_t &ctx, jl_value_t *sym) Value *box_isnull = ctx.builder.CreateICmpNE(boxed, Constant::getNullValue(ctx.types().T_prjlvalue)); if (vi.pTIndex) { // value is either boxed in the stack slot, or unboxed in value - // as indicated by testing (pTIndex & 0x80) + // as indicated by testing (pTIndex & UNION_BOX_MARKER) Value *tindex = ctx.builder.CreateAlignedLoad(getInt8Ty(ctx.builder.getContext()), vi.pTIndex, Align(sizeof(void*)), vi.isVolatile); Value *load_unbox = ctx.builder.CreateICmpEQ( - ctx.builder.CreateAnd(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)), + ctx.builder.CreateAnd(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)), ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)); isnull = ctx.builder.CreateSelect(load_unbox, isnull, box_isnull); } @@ -5067,9 +5076,9 @@ static jl_cgval_t emit_varinfo(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_sym_t *va maybe_mark_load_dereferenceable(boxed, vi.usedUndef || vi.pTIndex, typ); if (vi.pTIndex) { // value is either boxed in the stack slot, or unboxed in value - // as indicated by testing (pTIndex & 0x80) + // as indicated by testing (pTIndex & UNION_BOX_MARKER) Value *load_unbox = ctx.builder.CreateICmpEQ( - ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)), + ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)), ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)); if (vi.usedUndef) isnull = ctx.builder.CreateSelect(load_unbox, isnull, box_isnull); @@ -5200,7 +5209,7 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) PHINode *ptr_phi = PHINode::Create(ctx.types().T_prjlvalue, jl_array_nrows(edges), "ptr_phi"); BB->getInstList().insert(InsertPt, ptr_phi); Value *isboxed = ctx.builder.CreateICmpNE( - ctx.builder.CreateAnd(Tindex_phi, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)), + ctx.builder.CreateAnd(Tindex_phi, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)), ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)); ctx.builder.CreateMemCpy(phi, MaybeAlign(min_align), dest, dest->getAlign(), nbytes, false); ctx.builder.CreateLifetimeEnd(dest); @@ -5301,9 +5310,13 @@ static void emit_varinfo_assign(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_cgval_t // If allow_mismatch is set, type mismatches will not result in traps. // This is used for upsilon nodes, where the destination can have a narrower // type than the store, if inference determines that the store is never read. - Value *dummy = NULL; - Value **skip = allow_mismatch ? &dummy : NULL; - rval_info = convert_julia_type(ctx, rval_info, slot_type, skip); + Value *skip = NULL; + rval_info = convert_julia_type(ctx, rval_info, slot_type, &skip); + if (!allow_mismatch && skip) { + CreateTrap(ctx.builder); + return; + } + if (rval_info.typ == jl_bottom_type) return; @@ -5313,13 +5326,13 @@ static void emit_varinfo_assign(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_cgval_t if (rval_info.TIndex) { tindex = rval_info.TIndex; if (!vi.boxroot) - tindex = ctx.builder.CreateAnd(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x7f)); + tindex = ctx.builder.CreateAnd(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), ~UNION_BOX_MARKER)); } else { assert(rval_info.isboxed || rval_info.constant); tindex = compute_tindex_unboxed(ctx, rval_info, vi.value.typ); if (vi.boxroot) - tindex = ctx.builder.CreateOr(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)); + tindex = ctx.builder.CreateOr(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)); else rval_info.TIndex = tindex; } @@ -5333,7 +5346,7 @@ static void emit_varinfo_assign(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_cgval_t if (vi.pTIndex && rval_info.TIndex) { ctx.builder.CreateStore(rval_info.TIndex, vi.pTIndex, vi.isVolatile); isboxed = ctx.builder.CreateICmpNE( - ctx.builder.CreateAnd(rval_info.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)), + ctx.builder.CreateAnd(rval_info.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)), ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)); rval = rval_info.Vboxed ? rval_info.Vboxed : Constant::getNullValue(ctx.types().T_prjlvalue); assert(rval->getType() == ctx.types().T_prjlvalue); @@ -5348,8 +5361,13 @@ static void emit_varinfo_assign(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_cgval_t // store unboxed variables if (!vi.boxroot || (vi.pTIndex && rval_info.TIndex)) { - emit_vi_assignment_unboxed(ctx, vi, isboxed, rval_info); + emit_guarded_test(ctx, skip ? ctx.builder.CreateNot(skip) : nullptr, nullptr, [&]{ + emit_vi_assignment_unboxed(ctx, vi, isboxed, rval_info); + return nullptr; + }); } + + return; } static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r, ssize_t ssaval) @@ -5361,7 +5379,8 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r, ssi int sl = jl_slot_number(l) - 1; // it's a local variable jl_varinfo_t &vi = ctx.slots[sl]; - return emit_varinfo_assign(ctx, vi, rval_info, l); + emit_varinfo_assign(ctx, vi, rval_info, l); + return; } jl_module_t *mod; @@ -5392,15 +5411,17 @@ static void emit_upsilonnode(jl_codectx_t &ctx, ssize_t phic, jl_value_t *val) // upsilon node is not dynamically observed. if (val) { jl_cgval_t rval_info = emit_expr(ctx, val); - if (rval_info.typ == jl_bottom_type) + if (rval_info.typ == jl_bottom_type) { // as a special case, PhiC nodes are allowed to use undefined // values, since they are just copy operations, so we need to // ignore the store (it will not by dynamically observed), while // normally, for any other operation result, we'd assume this store // was unreachable and dead val = NULL; - else + } + else { emit_varinfo_assign(ctx, vi, rval_info, NULL, true); + } } if (!val) { if (vi.boxroot) { @@ -5412,7 +5433,7 @@ static void emit_upsilonnode(jl_codectx_t &ctx, ssize_t phic, jl_value_t *val) // does need to satisfy the union invariants (i.e. inbounds // tindex). ctx.builder.CreateAlignedStore( - vi.boxroot ? ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80) : + vi.boxroot ? ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER) : ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x01), vi.pTIndex, Align(1), true); } @@ -6195,7 +6216,7 @@ static void emit_cfunc_invalidate( Type *retty = gf_thunk->getReturnType(); Value *gf_retval = UndefValue::get(retty); Value *tindex = compute_box_tindex(ctx, emit_typeof(ctx, gf_retbox, false, true), (jl_value_t*)jl_any_type, rettype); - tindex = ctx.builder.CreateOr(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)); + tindex = ctx.builder.CreateOr(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)); gf_retval = ctx.builder.CreateInsertValue(gf_retval, gf_ret, 0); gf_retval = ctx.builder.CreateInsertValue(gf_retval, tindex, 1); ctx.builder.CreateRet(gf_retval); @@ -6691,7 +6712,7 @@ static Function* gen_cfun_wrapper( Value *tindex = ctx.builder.CreateExtractValue(call, 1); Value *derived = ctx.builder.CreateSelect( ctx.builder.CreateICmpEQ( - ctx.builder.CreateAnd(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)), + ctx.builder.CreateAnd(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)), ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)), decay_derived(ctx, ctx.builder.CreateBitCast(result, ctx.types().T_pjlvalue)), decay_derived(ctx, box)); @@ -8517,7 +8538,7 @@ static jl_llvm_functions_t // also need to account for the possibility the return object is boxed // and avoid / skip copying it to the stack isboxed_union = ctx.builder.CreateICmpNE( - ctx.builder.CreateAnd(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)), + ctx.builder.CreateAnd(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)), ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)); data = ctx.builder.CreateSelect(isboxed_union, retvalinfo.Vboxed, data); } @@ -8526,7 +8547,7 @@ static jl_llvm_functions_t // treat this as a simple boxed returninfo //assert(retvalinfo.isboxed); tindex = compute_tindex_unboxed(ctx, retvalinfo, jlrettype); - tindex = ctx.builder.CreateOr(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)); + tindex = ctx.builder.CreateOr(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)); data = boxed(ctx, retvalinfo); sret = NULL; } @@ -8770,7 +8791,7 @@ static jl_llvm_functions_t if (tindex == 0) { if (VN) V = boxed(ctx, val); - RTindex = ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80); + RTindex = ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER); } else { if (VN) @@ -8792,7 +8813,7 @@ static jl_llvm_functions_t if (dest) { // If dest is not set, this is a ghost union, the recipient of which // is often not prepared to handle a boxed representation of the ghost. - RTindex = ctx.builder.CreateOr(RTindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)); + RTindex = ctx.builder.CreateOr(RTindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)); } new_union.TIndex = RTindex; } @@ -8800,8 +8821,8 @@ static jl_llvm_functions_t V = new_union.Vboxed ? new_union.Vboxed : Constant::getNullValue(ctx.types().T_prjlvalue); if (dest) { // basically, if !ghost union if (new_union.Vboxed != nullptr) { - Value *isboxed = ctx.builder.CreateICmpNE( // if 0x80 is set, we won't select this slot anyways - ctx.builder.CreateAnd(RTindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)), + Value *isboxed = ctx.builder.CreateICmpNE( // if UNION_BOX_MARKER is set, we won't select this slot anyways + ctx.builder.CreateAnd(RTindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)), ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)); skip = skip ? ctx.builder.CreateOr(isboxed, skip) : isboxed; } @@ -8846,7 +8867,7 @@ static jl_llvm_functions_t Value *undef = undef_value_for_type(VN->getType()); VN->addIncoming(undef, FromBB); if (TindexN) // let the runtime / optimizer know this is unknown / boxed / null, so that it won't try to union_move / copy it later - RTindex = ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80); + RTindex = ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER); } if (TindexN) TindexN->addIncoming(RTindex, FromBB); diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 1da6c11b2a58a..f8a367b8342da 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -533,9 +533,9 @@ function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef, sv::CC.InferenceState) if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_graph_uncached(sv)) if CC.isdefined_globalref(g) - return Const(ccall(:jl_get_globalref_value, Any, (Any,), g)) + return CC.RTEffects(Const(ccall(:jl_get_globalref_value, Any, (Any,), g)), CC.EFFECTS_TOTAL) end - return Union{} + return CC.RTEffects(Union{}, CC.EFFECTS_THROWS) end return @invoke CC.abstract_eval_globalref(interp::CC.AbstractInterpreter, g::GlobalRef, sv::CC.InferenceState) diff --git a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl index fc2d6f774cd9d..d8ea8be21fe07 100644 --- a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl +++ b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl @@ -201,6 +201,7 @@ end let # try/catch result = code_escapes((Any,)) do a try + println("prevent ConstABI") nothing catch err return a # return escape @@ -210,6 +211,7 @@ end end let result = code_escapes((Any,)) do a try + println("prevent ConstABI") nothing finally return a # return escape diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index daa636a11b02d..a487d628f9f48 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -487,12 +487,16 @@ function f37262(x) catch GC.safepoint() end + local a try GC.gc() - return g37262(x) + a = g37262(x) + Base.inferencebarrier(false) && error() + return a catch ex GC.gc() finally + @isdefined(a) && Base.donotdelete(a) GC.gc() end end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 861bbbbcec36d..bfb78d6c61bfd 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2561,7 +2561,7 @@ function h25579(g) return t ? typeof(h) : typeof(h) end @test Base.return_types(h25579, (Base.RefValue{Union{Nothing, Int}},)) == - Any[Union{Type{Float64}, Type{Int}, Type{Nothing}}] + Any[Type{Float64}] f26172(v) = Val{length(Base.tail(ntuple(identity, v)))}() # Val(M-1) g26172(::Val{0}) = () @@ -4424,7 +4424,7 @@ end nothing end return x - end) === Union{Int, Float64, Char} + end) === Union{Int, Char} # issue #42097 struct Foo42097{F} end @@ -5372,6 +5372,123 @@ end @test Base.return_types(phic_type3) |> only === Union{Int, Float64} @test phic_type3() === 2 +# Issue #51852 +function phic_type4() + a = (;progress = "a") + try + may_error(false) + let b = Base.inferencebarrier(true) ? (;progress = 1.0) : a + a = b + end + catch + end + GC.gc() + return a +end +@test Base.return_types(phic_type4) |> only === Union{@NamedTuple{progress::Float64}, @NamedTuple{progress::String}} +@test phic_type4() === (;progress = 1.0) + +function phic_type5() + a = (;progress = "a") + try + vals = (a, (progress=1.0,)) + may_error(false) + a = vals[Base.inferencebarrier(false) ? 1 : 2] + catch + end + GC.gc() + return a +end +@test Base.return_types(phic_type5) |> only === Union{@NamedTuple{progress::Float64}, @NamedTuple{progress::String}} +@test phic_type5() === (;progress = 1.0) + +function phic_type6() + a = Base.inferencebarrier(true) ? (;progress = "a") : (;progress = Ref{Any}(0)) + try + may_error(false) + let b = Base.inferencebarrier(true) ? (;progress = 1.0) : a + a = b + end + catch + end + GC.gc() + return a +end +@test Base.return_types(phic_type6) |> only === Union{@NamedTuple{progress::Float64}, @NamedTuple{progress::Base.RefValue{Any}}, @NamedTuple{progress::String}} +@test phic_type6() === (;progress = 1.0) + +function phic_type7() + a = Base.inferencebarrier(true) ? (;progress = "a") : (;progress = Ref{Any}(0)) + try + vals = (a, (progress=1.0,)) + may_error(false) + a = vals[Base.inferencebarrier(false) ? 1 : 2] + catch + end + GC.gc() + return a +end +@test Base.return_types(phic_type7) |> only === Union{@NamedTuple{progress::Float64}, @NamedTuple{progress::Base.RefValue{Any}}, @NamedTuple{progress::String}} +@test phic_type7() === (;progress = 1.0) + +function phic_type8() + local a + try + may_error(true) + a = Base.inferencebarrier(1) + catch + end + + try + a = 2 + may_error(true) + catch + end + GC.gc() + return a +end +@test Base.return_types(phic_type8) |> only === Int +@test phic_type8() === 2 + + +function phic_type9() + local a + try + may_error(false) + a = Base.inferencebarrier(false) ? 1 : nothing + catch + end + + try + a = 2 + may_error(true) + catch + end + GC.gc() + return a +end +@test Base.return_types(phic_type9) |> only === Int +@test phic_type9() === 2 + +function phic_type10() + local a + try + may_error(false) + a = Base.inferencebarrier(true) ? missing : nothing + catch + end + + try + Base.inferencebarrier(true) && (a = 2) + may_error(true) + catch + end + GC.gc() + return a::Int +end +@test Base.return_types(phic_type10) |> only === Int +@test phic_type10() === 2 + # Test that `exit` returns `Union{}` (issue #51856) function test_exit_bottom(s) n = tryparse(Int, s) diff --git a/test/show.jl b/test/show.jl index 934a4bbd2ce48..899327d80b531 100644 --- a/test/show.jl +++ b/test/show.jl @@ -2052,6 +2052,7 @@ eval(Meta._parse_string("""function my_fun28173(x) r = 1 s = try r = 2 + Base.inferencebarrier(false) && error() "BYE" catch r = 3 @@ -2088,16 +2089,16 @@ let src = code_typed(my_fun28173, (Int,), debuginfo=:source)[1][1] end @test popfirst!(lines2) == " │ $(QuoteNode(2))" @test pop!(lines2) == " └─── \$(QuoteNode(4))" - @test pop!(lines1) == "17 └─── return %18" - @test pop!(lines2) == " │ return %18" - @test pop!(lines2) == "17 │ \$(QuoteNode(3))" + @test pop!(lines1) == "18 └─── return %22" + @test pop!(lines2) == " │ return %22" + @test pop!(lines2) == "18 │ \$(QuoteNode(3))" @test lines1 == lines2 # verbose linetable io = IOBuffer() Base.IRShow.show_ir(io, ir, Base.IRShow.default_config(ir; verbose_linetable=true)) seekstart(io) - @test count(contains(r"@ a{80}:\d+ within `my_fun28173"), eachline(io)) == 10 + @test count(contains(r"@ a{80}:\d+ within `my_fun28173"), eachline(io)) == 11 # Test that a bad :invoke doesn't cause an error during printing Core.Compiler.insert_node!(ir, 1, Core.Compiler.NewInstruction(Expr(:invoke, nothing, sin), Any), false)