@@ -628,11 +628,11 @@ static const auto jl_excstack_state_func = new JuliaFunction{
628628 [](LLVMContext &C) { return FunctionType::get (T_size, false ); },
629629 nullptr ,
630630};
631- static const auto jlegal_func = new JuliaFunction{
632- " jl_egal " ,
631+ static const auto jlegalx_func = new JuliaFunction{
632+ " jl_egal__unboxed " ,
633633 [](LLVMContext &C) {
634- Type *T = PointerType::get (T_jlvalue, AddressSpace::CalleeRooted );
635- return FunctionType::get (T_int32, {T, T}, false ); },
634+ Type *T = PointerType::get (T_jlvalue, AddressSpace::Derived );
635+ return FunctionType::get (T_int32, {T, T, T_prjlvalue }, false ); },
636636 [](LLVMContext &C) { return AttributeList::get (C,
637637 Attributes (C, {Attribute::ReadOnly, Attribute::NoUnwind, Attribute::ArgMemOnly}),
638638 AttributeSet (),
@@ -2411,10 +2411,10 @@ static Value *emit_box_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const
24112411 Value *nullcheck1, Value *nullcheck2)
24122412{
24132413 if (jl_pointer_egal (arg1.typ ) || jl_pointer_egal (arg2.typ )) {
2414+ assert ((arg1.isboxed || arg1.constant ) && (arg2.isboxed || arg2.constant ) &&
2415+ " Expected unboxed cases to be handled earlier" );
24142416 Value *varg1 = arg1.constant ? literal_pointer_val (ctx, arg1.constant ) : arg1.V ;
24152417 Value *varg2 = arg2.constant ? literal_pointer_val (ctx, arg2.constant ) : arg2.V ;
2416- assert (varg1 && varg2 && (arg1.isboxed || arg1.TIndex ) && (arg2.isboxed || arg2.TIndex ) &&
2417- " Only boxed types are valid for pointer comparison." );
24182418 varg1 = maybe_decay_tracked (ctx, varg1);
24192419 varg2 = maybe_decay_tracked (ctx, varg2);
24202420 if (cast<PointerType>(varg1->getType ())->getAddressSpace () != cast<PointerType>(varg2->getType ())->getAddressSpace ()) {
@@ -2426,10 +2426,19 @@ static Value *emit_box_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const
24262426 }
24272427
24282428 return emit_nullcheck_guard2 (ctx, nullcheck1, nullcheck2, [&] {
2429- Value *varg1 = mark_callee_rooted (ctx, boxed (ctx, arg1));
2430- Value *varg2 = mark_callee_rooted (ctx, boxed (ctx, arg2));
2431- return ctx.builder .CreateTrunc (ctx.builder .CreateCall (prepare_call (jlegal_func),
2432- {varg1, varg2}), T_int1);
2429+ Value *varg1 = arg1.constant ? literal_pointer_val (ctx, arg1.constant ) : maybe_bitcast (ctx, value_to_pointer (ctx, arg1).V , T_pjlvalue);
2430+ Value *varg2 = arg2.constant ? literal_pointer_val (ctx, arg2.constant ) : maybe_bitcast (ctx, value_to_pointer (ctx, arg2).V , T_pjlvalue);
2431+ varg1 = decay_derived (ctx, varg1);
2432+ varg2 = decay_derived (ctx, varg2);
2433+ Value *neq = ctx.builder .CreateICmpNE (varg1, varg2);
2434+ return emit_guarded_test (ctx, neq, true , [&] {
2435+ Value *dtarg = emit_typeof_boxed (ctx, arg1);
2436+ Value *dt_eq = ctx.builder .CreateICmpEQ (dtarg, emit_typeof_boxed (ctx, arg2));
2437+ return emit_guarded_test (ctx, dt_eq, false , [&] {
2438+ return ctx.builder .CreateTrunc (ctx.builder .CreateCall (prepare_call (jlegalx_func),
2439+ {varg1, varg2, dtarg}), T_int1);
2440+ });
2441+ });
24332442 });
24342443}
24352444
@@ -2583,6 +2592,7 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t a
25832592// representing the undef-ness of `arg1` and `arg2`.
25842593// This can only happen when comparing two fields of the same time and the result should be
25852594// true if both are NULL
2595+ // Like the runtime counterpart, this is codegen guaranteed to be non-allocating and to exclude safepoints
25862596static Value *emit_f_is (jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2,
25872597 Value *nullcheck1, Value *nullcheck2)
25882598{
@@ -2603,46 +2613,45 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva
26032613 // since it is normalized to `::Type{Union{}}` instead...
26042614 if (arg1.TIndex )
26052615 return emit_nullcheck_guard (ctx, nullcheck1, [&] {
2606- return emit_isa (ctx, arg1, rt2, NULL ). first ; // rt2 is a singleton type
2616+ return emit_exactly_isa (ctx, arg1, rt2) ; // rt2 is a singleton type
26072617 });
26082618 if (arg2.TIndex )
26092619 return emit_nullcheck_guard (ctx, nullcheck2, [&] {
2610- return emit_isa (ctx, arg2, rt1, NULL ). first ; // rt1 is a singleton type
2620+ return emit_exactly_isa (ctx, arg2, rt1) ; // rt1 is a singleton type
26112621 });
2622+ if (!(arg1.isboxed || arg1.constant ) || !(arg2.isboxed || arg2.constant ))
2623+ // not TIndex && not boxed implies it is an unboxed value of a different type from this singleton
2624+ // (which was probably caught above, but just to be safe, we repeat it here explicitly)
2625+ return ConstantInt::get (T_int1, 0 );
2626+ Value *varg1 = arg1.constant ? literal_pointer_val (ctx, arg1.constant ) : maybe_bitcast (ctx, arg1.Vboxed , T_pjlvalue);
2627+ Value *varg2 = arg2.constant ? literal_pointer_val (ctx, arg2.constant ) : maybe_bitcast (ctx, arg2.Vboxed , T_pjlvalue);
26122628 // rooting these values isn't needed since we won't load this pointer
26132629 // and we know at least one of them is a unique Singleton
26142630 // which is already enough to ensure pointer uniqueness for this test
26152631 // even if the other pointer managed to get garbage collected
2616- return ctx.builder .CreateICmpEQ (
2617- mark_callee_rooted (ctx, boxed (ctx, arg1)),
2618- mark_callee_rooted (ctx, boxed (ctx, arg2)));
2632+ // TODO: use emit_pointer_from_objref instead, per comment above
2633+ return ctx.builder .CreateICmpEQ (decay_derived (ctx, varg1), decay_derived (ctx, varg2));
26192634 }
26202635
26212636 if (jl_type_intersection (rt1, rt2) == (jl_value_t *)jl_bottom_type) // types are disjoint (exhaustive test)
26222637 return ConstantInt::get (T_int1, 0 );
26232638
2624- // If both sides are boxed or can be trivially boxed,
2625- // we'll prefer to do a pointer check.
2626- // At this point, we know that at least one of the arguments isn't a constant
2627- // so a runtime content check will involve at least one load from the
2628- // pointer (and likely a type check)
2629- // so a pointer comparison should be no worse than that even in imaging mode
2630- // when the constant pointer has to be loaded.
2631- if ((arg1.V || arg1.constant ) && (arg2.V || arg2.constant ) &&
2632- (jl_pointer_egal (rt1) || jl_pointer_egal (rt2)) &&
2633- // jl_pointer_egal returns true for Bool, which is not helpful here
2634- (rt1 != (jl_value_t *)jl_bool_type || rt2 != (jl_value_t *)jl_bool_type))
2635- return ctx.builder .CreateICmpEQ (boxed (ctx, arg1), boxed (ctx, arg2));
2636-
26372639 bool justbits1 = jl_is_concrete_immutable (rt1);
26382640 bool justbits2 = jl_is_concrete_immutable (rt2);
26392641 if (justbits1 || justbits2) { // whether this type is unique'd by value
26402642 return emit_nullcheck_guard2 (ctx, nullcheck1, nullcheck2, [&] () -> Value* {
26412643 jl_value_t *typ = justbits1 ? rt1 : rt2;
2644+ if (typ == (jl_value_t *)jl_bool_type) { // aka jl_pointer_egal
2645+ // some optimizations for bool, since pointer comparison may be better
2646+ if ((arg1.isboxed || arg1.constant ) && (arg2.isboxed || arg2.constant )) { // aka have-fast-pointer
2647+ Value *varg1 = arg1.constant ? literal_pointer_val (ctx, arg1.constant ) : maybe_bitcast (ctx, arg1.Vboxed , T_pjlvalue);
2648+ Value *varg2 = arg2.constant ? literal_pointer_val (ctx, arg2.constant ) : maybe_bitcast (ctx, arg2.Vboxed , T_pjlvalue);
2649+ return ctx.builder .CreateICmpEQ (decay_derived (ctx, varg1), decay_derived (ctx, varg2));
2650+ }
2651+ }
26422652 if (rt1 == rt2)
26432653 return emit_bits_compare (ctx, arg1, arg2);
2644- Value *same_type = (typ == rt2) ? emit_isa (ctx, arg1, typ, NULL ).first :
2645- emit_isa (ctx, arg2, typ, NULL ).first ;
2654+ Value *same_type = emit_exactly_isa (ctx, (typ == rt2 ? arg1 : arg2), typ);
26462655 BasicBlock *currBB = ctx.builder .GetInsertBlock ();
26472656 BasicBlock *isaBB = BasicBlock::Create (jl_LLVMContext, " is" , ctx.f );
26482657 BasicBlock *postBB = BasicBlock::Create (jl_LLVMContext, " post_is" , ctx.f );
@@ -2660,6 +2669,25 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva
26602669 });
26612670 }
26622671
2672+ // If either sides is boxed or can be trivially boxed,
2673+ // we'll prefer to do a pointer check.
2674+ // At this point, we know that at least one of the arguments isn't a constant
2675+ // so a runtime content check will involve at least one load from the
2676+ // pointer (and likely a type check)
2677+ // so a pointer comparison should be no worse than that even in imaging mode
2678+ // when the constant pointer has to be loaded.
2679+ // Note that we ignore nullcheck, since in the case where it may be set, we
2680+ // also knew the types of both fields must be the same so there cannot be
2681+ // any unboxed values on either side.
2682+ if (jl_pointer_egal (rt1) || jl_pointer_egal (rt2)) {
2683+ // n.b. Vboxed == isboxed || Tindex
2684+ if (!(arg1.Vboxed || arg1.constant ) || !(arg2.Vboxed || arg2.constant ))
2685+ return ConstantInt::get (T_int1, 0 );
2686+ Value *varg1 = arg1.constant ? literal_pointer_val (ctx, arg1.constant ) : maybe_bitcast (ctx, arg1.Vboxed , T_pjlvalue);
2687+ Value *varg2 = arg2.constant ? literal_pointer_val (ctx, arg2.constant ) : maybe_bitcast (ctx, arg2.Vboxed , T_pjlvalue);
2688+ return ctx.builder .CreateICmpEQ (decay_derived (ctx, varg1), decay_derived (ctx, varg2));
2689+ }
2690+
26632691 // TODO: handle the case where arg1.typ != arg2.typ, or when one of these isn't union,
26642692 // or when the union can be pointer
26652693 if (arg1.TIndex && arg2.TIndex && jl_egal (arg1.typ , arg2.typ ) &&
@@ -3498,8 +3526,7 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_
34983526 argvals[idx] = boxed (ctx, arg);
34993527 }
35003528 else if (et->isAggregateType ()) {
3501- if (!arg.ispointer ())
3502- arg = value_to_pointer (ctx, arg);
3529+ arg = value_to_pointer (ctx, arg);
35033530 // can lazy load on demand, no copy needed
35043531 assert (at == PointerType::get (et, AddressSpace::Derived));
35053532 argvals[idx] = decay_derived (ctx, maybe_bitcast (ctx,
@@ -5437,8 +5464,7 @@ static Function* gen_cfun_wrapper(
54375464 }
54385465 else if (T->isAggregateType ()) {
54395466 // aggregate types are passed by pointer
5440- if (!inputarg.ispointer ())
5441- inputarg = value_to_pointer (ctx, inputarg);
5467+ inputarg = value_to_pointer (ctx, inputarg);
54425468 arg = maybe_bitcast (ctx, decay_derived (ctx, data_pointer (ctx, inputarg)),
54435469 T->getPointerTo ());
54445470 }
@@ -7977,7 +8003,7 @@ static void init_jit_functions(void)
79778003 add_named_global (jlleave_func, &jl_pop_handler);
79788004 add_named_global (jl_restore_excstack_func, &jl_restore_excstack);
79798005 add_named_global (jl_excstack_state_func, &jl_excstack_state);
7980- add_named_global (jlegal_func , &jl_egal );
8006+ add_named_global (jlegalx_func , &jl_egal__unboxed );
79818007 add_named_global (jlisa_func, &jl_isa);
79828008 add_named_global (jlsubtype_func, &jl_subtype);
79838009 add_named_global (jltypeassert_func, &jl_typeassert);
0 commit comments