@@ -2165,6 +2165,107 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas
21652165 return TRUE;
21662166 } else if (!strcmp (tm , "AreSame ")) {
21672167 * op = MINT_CEQ_P ;
2168+ } else if (!strcmp (tm , "BitCast ")) {
2169+ MonoGenericContext * ctx = mono_method_get_context (target_method );
2170+ g_assert (ctx );
2171+ g_assert (ctx -> method_inst );
2172+ g_assert (ctx -> method_inst -> type_argc == 2 );
2173+ g_assert (csignature -> param_count == 1 );
2174+
2175+ // We explicitly do not handle gsharedvt as it is meant as a slow fallback strategy
2176+ // instead we fallback to the managed implementation which will do the right things
2177+
2178+ MonoType * tfrom = ctx -> method_inst -> type_argv [0 ];
2179+ if (mini_is_gsharedvt_variable_type (tfrom )) {
2180+ return FALSE;
2181+ }
2182+
2183+ MonoType * tto = ctx -> method_inst -> type_argv [1 ];
2184+ if (mini_is_gsharedvt_variable_type (tto )) {
2185+ return FALSE;
2186+ }
2187+
2188+ // The underlying API always throws for reference type inputs, so we
2189+ // fallback to the managed implementation to let that handling occur
2190+
2191+ MonoTypeEnum tfrom_type = tfrom -> type ;
2192+ if (MONO_TYPE_IS_REFERENCE (tfrom )) {
2193+ return FALSE;
2194+ }
2195+
2196+ MonoTypeEnum tto_type = tto -> type ;
2197+ if (MONO_TYPE_IS_REFERENCE (tto )) {
2198+ return FALSE;
2199+ }
2200+
2201+ MonoClass * tfrom_klass = mono_class_from_mono_type_internal (tfrom );
2202+ if (mono_class_is_nullable (tfrom_klass )) {
2203+ return FALSE;
2204+ }
2205+
2206+ // We also always throw for Nullable<T> inputs, so fallback to the
2207+ // managed implementation here as well.
2208+
2209+ MonoClass * tto_klass = mono_class_from_mono_type_internal (tto );
2210+ if (mono_class_is_nullable (tto_klass )) {
2211+ return FALSE;
2212+ }
2213+
2214+ // The same applies for when the type sizes do not match, as this will always throw
2215+ // and so its not an expected case and we can fallback to the managed implementation
2216+
2217+ int tfrom_align , tto_align ;
2218+ if (mono_type_size (tfrom , & tfrom_align ) != mono_type_size (tto , & tto_align )) {
2219+ return FALSE;
2220+ }
2221+
2222+ // We have several different move opcodes to handle the data depending on the
2223+ // source and target types, so detect and optimize the most common ones falling
2224+ // back to what is effectively `ReadUnaligned<TTo>(ref As<TFrom, byte>(ref source))`
2225+ // for anything that can't be special cased as potentially zero-cost move.
2226+
2227+ if (tfrom_type == MONO_TYPE_I4 ) {
2228+ if (tto_type == MONO_TYPE_R4 ) {
2229+ * op = MINT_BITCAST_R4_I4 ;
2230+ break ;
2231+ } else if (tto_type == MONO_TYPE_I4 ) {
2232+ * op = MINT_MOV_4 ;
2233+ break ;
2234+ }
2235+ } else if (tfrom_type == MONO_TYPE_I8 ) {
2236+ if (tto_type == MONO_TYPE_R8 ) {
2237+ * op = MINT_BITCAST_R8_I8 ;
2238+ break ;
2239+ } else if (tto_type == MONO_TYPE_I8 ) {
2240+ * op = MINT_MOV_8 ;
2241+ break ;
2242+ }
2243+ } else if (tfrom_type == MONO_TYPE_R4 ) {
2244+ if (tto_type == MONO_TYPE_I4 ) {
2245+ * op = MINT_BITCAST_I4_R4 ;
2246+ break ;
2247+ } else if (tto_type == MONO_TYPE_R4 ) {
2248+ * op = MINT_MOV_4 ;
2249+ break ;
2250+ }
2251+ } else if (tfrom_type == MONO_TYPE_R8 ) {
2252+ if (tto_type == MONO_TYPE_I8 ) {
2253+ * op = MINT_BITCAST_I8_R8 ;
2254+ break ;
2255+ } else if (tto_type == MONO_TYPE_R8 ) {
2256+ * op = MINT_MOV_8 ;
2257+ break ;
2258+ }
2259+ }
2260+
2261+ gint32 size = mono_class_value_size (tfrom_klass , NULL );
2262+ g_assert (size < G_MAXUINT16 );
2263+
2264+ interp_add_ins (td , MINT_MOV_VT );
2265+ interp_ins_set_sreg (td -> last_ins , td -> sp [-1 ].var );
2266+ push_type_vt (td , tto_klass , size );
2267+ interp_ins_set_dreg (td -> last_ins , td -> sp [-1 ].var );
2268+ td -> last_ins -> data [0 ] = GINT32_TO_UINT16 (size );
21682269 } else if (!strcmp (tm , "ByteOffset ")) {
21692270#if SIZEOF_VOID_P == 4
21702271 interp_add_ins (td , MINT_SUB_I4 );
0 commit comments