@@ -427,6 +427,40 @@ emit_span_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature
427
427
return NULL ;
428
428
}
429
429
430
+ static MonoInst *
431
+ emit_bitconverter_intrinsics (MonoCompile * cfg , MonoMethod * cmethod , MonoMethodSignature * fsig , MonoInst * * args )
432
+ {
433
+ MonoInst * ins ;
434
+
435
+ if (!strcmp (cmethod -> name , "DoubleToInt64Bits" ) || !strcmp (cmethod -> name , "DoubleToUInt64Bits" )) {
436
+ g_assert (fsig -> param_count == 1 );
437
+ int dreg = mono_alloc_dreg (cfg , STACK_I8 );
438
+ EMIT_NEW_UNALU (cfg , ins , OP_MOVE_F_TO_I8 , dreg , args [0 ]-> dreg );
439
+ ins -> type = STACK_I8 ;
440
+ return ins ;
441
+ } else if (!strcmp (cmethod -> name , "Int32BitsToSingle" ) || !strcmp (cmethod -> name , "UInt32BitsToSingle" )) {
442
+ g_assert (fsig -> param_count == 1 );
443
+ int dreg = mono_alloc_dreg (cfg , STACK_R4 );
444
+ EMIT_NEW_UNALU (cfg , ins , OP_MOVE_I4_TO_F , dreg , args [0 ]-> dreg );
445
+ ins -> type = STACK_R4 ;
446
+ return ins ;
447
+ } else if (!strcmp (cmethod -> name , "Int64BitsToDouble" ) || !strcmp (cmethod -> name , "UInt64BitsToDouble" )) {
448
+ g_assert (fsig -> param_count == 1 );
449
+ int dreg = mono_alloc_dreg (cfg , STACK_R8 );
450
+ EMIT_NEW_UNALU (cfg , ins , OP_MOVE_I8_TO_F , dreg , args [0 ]-> dreg );
451
+ ins -> type = STACK_R8 ;
452
+ return ins ;
453
+ } else if (!strcmp (cmethod -> name , "SingleToInt32Bits" ) || !strcmp (cmethod -> name , "SingleToUInt32Bits" )) {
454
+ g_assert (fsig -> param_count == 1 );
455
+ int dreg = mono_alloc_dreg (cfg , STACK_I4 );
456
+ EMIT_NEW_UNALU (cfg , ins , OP_MOVE_F_TO_I4 , dreg , args [0 ]-> dreg );
457
+ ins -> type = STACK_I4 ;
458
+ return ins ;
459
+ }
460
+
461
+ return NULL ;
462
+ }
463
+
430
464
static MonoInst *
431
465
emit_unsafe_intrinsics (MonoCompile * cfg , MonoMethod * cmethod , MonoMethodSignature * fsig , MonoInst * * args )
432
466
{
@@ -488,6 +522,147 @@ emit_unsafe_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignatu
488
522
EMIT_NEW_BIALU (cfg , ins , OP_COMPARE , -1 , args [0 ]-> dreg , args [1 ]-> dreg );
489
523
EMIT_NEW_UNALU (cfg , ins , OP_PCEQ , dreg , -1 );
490
524
return ins ;
525
+ } else if (!strcmp (cmethod -> name , "BitCast" )) {
526
+ g_assert (ctx );
527
+ g_assert (ctx -> method_inst );
528
+ g_assert (ctx -> method_inst -> type_argc == 2 );
529
+ g_assert (fsig -> param_count == 1 );
530
+
531
+ // We explicitly do not handle gsharedvt as it is meant as a slow fallback strategy
532
+ // instead we fallback to the managed implementation which will do the right things
533
+
534
+ MonoType * tfrom = ctx -> method_inst -> type_argv [0 ];
535
+ if (mini_is_gsharedvt_variable_type (tfrom )) {
536
+ return NULL ;
537
+ }
538
+
539
+ MonoType * tto = ctx -> method_inst -> type_argv [1 ];
540
+ if (mini_is_gsharedvt_variable_type (tto )) {
541
+ return NULL ;
542
+ }
543
+
544
+ // The underlying API always throws for reference type inputs, so we
545
+ // fallback to the managed implementation to let that handling occur
546
+
547
+ MonoTypeEnum tfrom_type = tfrom -> type ;
548
+ if (MONO_TYPE_IS_REFERENCE (tfrom )) {
549
+ return NULL ;
550
+ }
551
+
552
+ MonoTypeEnum tto_type = tto -> type ;
553
+ if (MONO_TYPE_IS_REFERENCE (tto )) {
554
+ return NULL ;
555
+ }
556
+
557
+ // We also always throw for Nullable<T> inputs, so fallback to the
558
+ // managed implementation here as well.
559
+
560
+ MonoClass * tfrom_klass = mono_class_from_mono_type_internal (tfrom );
561
+ if (mono_class_is_nullable (tfrom_klass )) {
562
+ return NULL ;
563
+ }
564
+
565
+ MonoClass * tto_klass = mono_class_from_mono_type_internal (tto );
566
+ if (mono_class_is_nullable (tto_klass )) {
567
+ return NULL ;
568
+ }
569
+
570
+ // The same applies for when the type sizes do not match, as this will always throw
571
+ // and so its not an expected case and we can fallback to the managed implementation
572
+
573
+ int tfrom_align , tto_align ;
574
+ gint32 size = mono_type_size (tfrom , & tfrom_align );
575
+
576
+ if (size != mono_type_size (tto , & tto_align )) {
577
+ return FALSE;
578
+ }
579
+ g_assert (size < G_MAXUINT16 );
580
+
581
+ // We have several different move opcodes to handle the data depending on the
582
+ // source and target types, so detect and optimize the most common ones falling
583
+ // back to what is effectively `ReadUnaligned<TTo>(ref As<TFrom, byte>(ref source))`
584
+ // for anything that can't be special cased as potentially zero-cost move.
585
+
586
+ guint32 opcode = OP_LDADDR ;
587
+ MonoStackType tto_stack = STACK_OBJ ;
588
+
589
+ bool tfrom_is_primitive_or_enum = false;
590
+ if (m_class_is_primitive (tfrom_klass )) {
591
+ tfrom_is_primitive_or_enum = true;
592
+ } else if (m_class_is_enumtype (tfrom_klass )) {
593
+ tfrom_is_primitive_or_enum = true;
594
+ tfrom_type = mono_class_enum_basetype_internal (tfrom_klass )-> type ;
595
+ }
596
+
597
+ bool tto_is_primitive_or_enum = false;
598
+ if (m_class_is_primitive (tto_klass )) {
599
+ tto_is_primitive_or_enum = true;
600
+ } else if (m_class_is_enumtype (tto_klass )) {
601
+ tto_is_primitive_or_enum = true;
602
+ tto_type = mono_class_enum_basetype_internal (tto_klass )-> type ;
603
+ }
604
+
605
+ if (tfrom_is_primitive_or_enum && tto_is_primitive_or_enum ) {
606
+ if (size == 1 ) {
607
+ // opcode = OP_MOVE;
608
+ // tto_stack = STACK_I4;
609
+ return NULL ;
610
+ } else if (size == 2 ) {
611
+ // opcode = OP_MOVE;
612
+ // tto_stack = STACK_I4;
613
+ return NULL ;
614
+ } else if (size == 4 ) {
615
+ if ((tfrom_type == MONO_TYPE_R4 ) && ((tto_type == MONO_TYPE_I4 ) || (tto_type == MONO_TYPE_U4 ))) {
616
+ opcode = OP_MOVE_F_TO_I4 ;
617
+ tto_stack = STACK_I4 ;
618
+ } else if ((tto_type == MONO_TYPE_R4 ) && ((tfrom_type == MONO_TYPE_I4 ) || (tfrom_type == MONO_TYPE_U4 ))) {
619
+ opcode = OP_MOVE_I4_TO_F ;
620
+ tto_stack = STACK_R4 ;
621
+ } else {
622
+ opcode = OP_MOVE ;
623
+ tto_stack = STACK_I4 ;
624
+ }
625
+ } else if (size == 8 ) {
626
+ if ((tfrom_type == MONO_TYPE_R8 ) && ((tto_type == MONO_TYPE_I8 ) || (tto_type == MONO_TYPE_U8 ))) {
627
+ opcode = OP_MOVE_F_TO_I8 ;
628
+ tto_stack = STACK_I8 ;
629
+ } else if ((tto_type == MONO_TYPE_R8 ) && ((tfrom_type == MONO_TYPE_I8 ) || (tfrom_type == MONO_TYPE_U8 ))) {
630
+ opcode = OP_MOVE_I8_TO_F ;
631
+ tto_stack = STACK_R8 ;
632
+ } else {
633
+ opcode = OP_MOVE ;
634
+ tto_stack = STACK_I8 ;
635
+ }
636
+ }
637
+ } else if (mini_class_is_simd (cfg , tfrom_klass ) && mini_class_is_simd (cfg , tto_klass )) {
638
+ // int dreg = mono_alloc_dreg (cfg, STACK_VTYPE);
639
+ // EMIT_NEW_UNALU (cfg, ins, OP_XMOVE, dreg, args [0]->dreg);
640
+ // ins->type = STACK_VTYPE;
641
+ // ins->klass = tto_klass;
642
+ // return ins;
643
+ return NULL ;
644
+ }
645
+
646
+ if (opcode == OP_LDADDR ) {
647
+ // FIXME: This isn't quite right
648
+ //
649
+ // MonoInst *addr;
650
+ // EMIT_NEW_VARLOADA_VREG (cfg, addr, args [0]->dreg, tfrom);
651
+ // addr->klass = tfrom_klass;
652
+ //
653
+ // // We don't need to call mini_get_underlying_type on tto
654
+ // // since we have skipped handling for gsharedvt further up
655
+ // assert(MONO_TYPE_ISSTRUCT (tto));
656
+ //
657
+ // return mini_emit_memory_load (cfg, tto, addr, 0, MONO_INST_UNALIGNED);
658
+ return NULL ;
659
+ }
660
+
661
+ int dreg = mono_alloc_dreg (cfg , tto_stack );
662
+ EMIT_NEW_UNALU (cfg , ins , opcode , dreg , args [0 ]-> dreg );
663
+ ins -> type = tto_stack ;
664
+ ins -> klass = tto_klass ;
665
+ return ins ;
491
666
} else if (!strcmp (cmethod -> name , "IsAddressLessThan" )) {
492
667
g_assert (ctx );
493
668
g_assert (ctx -> method_inst );
@@ -2087,6 +2262,10 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign
2087
2262
!strcmp (cmethod_klass_name_space , "System" ) &&
2088
2263
(!strcmp (cmethod_klass_name , "Span`1" ) || !strcmp (cmethod_klass_name , "ReadOnlySpan`1" ))) {
2089
2264
return emit_span_intrinsics (cfg , cmethod , fsig , args );
2265
+ } else if (in_corlib &&
2266
+ !strcmp (cmethod_klass_name_space , "System" ) &&
2267
+ !strcmp (cmethod_klass_name , "BitConverter" )) {
2268
+ return emit_bitconverter_intrinsics (cfg , cmethod , fsig , args );
2090
2269
} else if (in_corlib &&
2091
2270
!strcmp (cmethod_klass_name_space , "System.Runtime.CompilerServices" ) &&
2092
2271
!strcmp (cmethod_klass_name , "Unsafe" )) {
0 commit comments