@@ -628,11 +628,11 @@ static const auto jl_excstack_state_func = new JuliaFunction{
628
628
[](LLVMContext &C) { return FunctionType::get (T_size, false ); },
629
629
nullptr ,
630
630
};
631
- static const auto jlegal_func = new JuliaFunction{
632
- " jl_egal " ,
631
+ static const auto jlegalx_func = new JuliaFunction{
632
+ " jl_egal__unboxed " ,
633
633
[](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 ); },
636
636
[](LLVMContext &C) { return AttributeList::get (C,
637
637
Attributes (C, {Attribute::ReadOnly, Attribute::NoUnwind, Attribute::ArgMemOnly}),
638
638
AttributeSet (),
@@ -2411,10 +2411,10 @@ static Value *emit_box_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const
2411
2411
Value *nullcheck1, Value *nullcheck2)
2412
2412
{
2413
2413
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" );
2414
2416
Value *varg1 = arg1.constant ? literal_pointer_val (ctx, arg1.constant ) : arg1.V ;
2415
2417
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." );
2418
2418
varg1 = maybe_decay_tracked (ctx, varg1);
2419
2419
varg2 = maybe_decay_tracked (ctx, varg2);
2420
2420
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
2426
2426
}
2427
2427
2428
2428
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
+ });
2433
2442
});
2434
2443
}
2435
2444
@@ -2583,6 +2592,7 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t a
2583
2592
// representing the undef-ness of `arg1` and `arg2`.
2584
2593
// This can only happen when comparing two fields of the same time and the result should be
2585
2594
// true if both are NULL
2595
+ // Like the runtime counterpart, this is codegen guaranteed to be non-allocating and to exclude safepoints
2586
2596
static Value *emit_f_is (jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2,
2587
2597
Value *nullcheck1, Value *nullcheck2)
2588
2598
{
@@ -2603,46 +2613,45 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva
2603
2613
// since it is normalized to `::Type{Union{}}` instead...
2604
2614
if (arg1.TIndex )
2605
2615
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
2607
2617
});
2608
2618
if (arg2.TIndex )
2609
2619
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
2611
2621
});
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);
2612
2628
// rooting these values isn't needed since we won't load this pointer
2613
2629
// and we know at least one of them is a unique Singleton
2614
2630
// which is already enough to ensure pointer uniqueness for this test
2615
2631
// 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));
2619
2634
}
2620
2635
2621
2636
if (jl_type_intersection (rt1, rt2) == (jl_value_t *)jl_bottom_type) // types are disjoint (exhaustive test)
2622
2637
return ConstantInt::get (T_int1, 0 );
2623
2638
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
-
2637
2639
bool justbits1 = jl_is_concrete_immutable (rt1);
2638
2640
bool justbits2 = jl_is_concrete_immutable (rt2);
2639
2641
if (justbits1 || justbits2) { // whether this type is unique'd by value
2640
2642
return emit_nullcheck_guard2 (ctx, nullcheck1, nullcheck2, [&] () -> Value* {
2641
2643
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
+ }
2642
2652
if (rt1 == rt2)
2643
2653
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);
2646
2655
BasicBlock *currBB = ctx.builder .GetInsertBlock ();
2647
2656
BasicBlock *isaBB = BasicBlock::Create (jl_LLVMContext, " is" , ctx.f );
2648
2657
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
2660
2669
});
2661
2670
}
2662
2671
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
+
2663
2691
// TODO: handle the case where arg1.typ != arg2.typ, or when one of these isn't union,
2664
2692
// or when the union can be pointer
2665
2693
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_
3498
3526
argvals[idx] = boxed (ctx, arg);
3499
3527
}
3500
3528
else if (et->isAggregateType ()) {
3501
- if (!arg.ispointer ())
3502
- arg = value_to_pointer (ctx, arg);
3529
+ arg = value_to_pointer (ctx, arg);
3503
3530
// can lazy load on demand, no copy needed
3504
3531
assert (at == PointerType::get (et, AddressSpace::Derived));
3505
3532
argvals[idx] = decay_derived (ctx, maybe_bitcast (ctx,
@@ -5437,8 +5464,7 @@ static Function* gen_cfun_wrapper(
5437
5464
}
5438
5465
else if (T->isAggregateType ()) {
5439
5466
// aggregate types are passed by pointer
5440
- if (!inputarg.ispointer ())
5441
- inputarg = value_to_pointer (ctx, inputarg);
5467
+ inputarg = value_to_pointer (ctx, inputarg);
5442
5468
arg = maybe_bitcast (ctx, decay_derived (ctx, data_pointer (ctx, inputarg)),
5443
5469
T->getPointerTo ());
5444
5470
}
@@ -7977,7 +8003,7 @@ static void init_jit_functions(void)
7977
8003
add_named_global (jlleave_func, &jl_pop_handler);
7978
8004
add_named_global (jl_restore_excstack_func, &jl_restore_excstack);
7979
8005
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 );
7981
8007
add_named_global (jlisa_func, &jl_isa);
7982
8008
add_named_global (jlsubtype_func, &jl_subtype);
7983
8009
add_named_global (jltypeassert_func, &jl_typeassert);
0 commit comments