@@ -2165,6 +2165,103 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas
2165
2165
return TRUE;
2166
2166
} else if (!strcmp (tm , "AreSame ")) {
2167
2167
* 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
+ } else if (tto_type == MONO_TYPE_I4 ) {
2231
+ * op = MINT_MOV_4 ;
2232
+ }
2233
+ } else if (tfrom_type == MONO_TYPE_I8 ) {
2234
+ if (tto_type == MONO_TYPE_R8 ) {
2235
+ * op = MINT_BITCAST_R8_I8 ;
2236
+ } else if (tto_type == MONO_TYPE_I8 ) {
2237
+ * op = MINT_MOV_8 ;
2238
+ }
2239
+ } else if (tfrom_type == MONO_TYPE_R4 ) {
2240
+ if (tto_type == MONO_TYPE_I4 ) {
2241
+ * op = MINT_BITCAST_I4_R4 ;
2242
+ } else if (tto_type == MONO_TYPE_R4 ) {
2243
+ * op = MINT_MOV_4 ;
2244
+ }
2245
+ } else if (tfrom_type == MONO_TYPE_R8 ) {
2246
+ if (tto_type == MONO_TYPE_I8 ) {
2247
+ * op = MINT_BITCAST_I8_R8 ;
2248
+ } else if (tto_type == MONO_TYPE_R8 ) {
2249
+ * op = MINT_MOV_8 ;
2250
+ }
2251
+ }
2252
+
2253
+ if (* op == -1 ) {
2254
+ gint32 size = mono_class_value_size (tfrom_klass , NULL );
2255
+ g_assert (size < G_MAXUINT16 );
2256
+
2257
+ interp_add_ins (td , MINT_MOV_VT );
2258
+ interp_ins_set_sreg (td -> last_ins , td -> sp [-1 ].var );
2259
+ push_type_vt (td , tto_klass , size );
2260
+ interp_ins_set_dreg (td -> last_ins , td -> sp [-1 ].var );
2261
+ td -> last_ins -> data [0 ] = GINT32_TO_UINT16 (size );
2262
+ td -> ip ++ ;
2263
+ return TRUE;
2264
+ }
2168
2265
} else if (!strcmp (tm , "ByteOffset ")) {
2169
2266
#if SIZEOF_VOID_P == 4
2170
2267
interp_add_ins (td , MINT_SUB_I4 );
0 commit comments