break;
}
+ case NI_System_Type_get_IsValueType:
+ {
+ // Optimize
+ //
+ // call Type.GetTypeFromHandle (which is replaced with CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE)
+ // call Type.IsValueType
+ //
+ // to `true` or `false`
+ // e.g. `typeof(int).IsValueType` => `true`
+ if (impStackTop().val->IsCall())
+ {
+ GenTreeCall* call = impStackTop().val->AsCall();
+ if (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE))
+ {
+ CORINFO_CLASS_HANDLE hClass = gtGetHelperArgClassHandle(call->gtCallArgs->GetNode());
+ if (hClass != NO_CLASS_HANDLE)
+ {
+ retNode =
+ gtNewIconNode((eeIsValueClass(hClass) &&
+ // pointers are not value types (e.g. typeof(int*).IsValueType is false)
+ info.compCompHnd->asCorInfoType(hClass) != CORINFO_TYPE_PTR)
+ ? 1
+ : 0);
+ impPopStack(); // drop CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE call
+ }
+ }
+ }
+ break;
+ }
+
#ifdef FEATURE_HW_INTRINSICS
case NI_System_Math_FusedMultiplyAdd:
case NI_System_MathF_FusedMultiplyAdd:
result = NI_System_GC_KeepAlive;
}
}
+ else if (strcmp(className, "Type") == 0)
+ {
+ if (strcmp(methodName, "get_IsValueType") == 0)
+ {
+ result = NI_System_Type_get_IsValueType;
+ }
+ }
}
#if defined(_TARGET_XARCH_) // We currently only support BSWAP on x86
else if (strcmp(namespaceName, "System.Buffers.Binary") == 0)
--- /dev/null
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
+
+public class Program
+{
+ private static int _errors = 0;
+
+ public static int Main(string[] args)
+ {
+ IsTrue (typeof(byte).IsValueType);
+ IsTrue (typeof(int).IsValueType);
+ IsTrue (typeof(int?).IsValueType);
+ IsFalse(typeof(int*).IsValueType);
+ IsFalse(typeof(int**).IsValueType);
+ IsFalse(typeof(void*).IsValueType);
+ IsFalse(typeof(void**).IsValueType);
+ IsFalse(typeof(GenericStruct<int>*).IsValueType);
+ IsTrue (typeof(IntPtr).IsValueType);
+ IsTrue (typeof(decimal).IsValueType);
+ IsTrue (typeof(double).IsValueType);
+ IsFalse(typeof(string).IsValueType);
+ IsFalse(typeof(object).IsValueType);
+ IsFalse(typeof(object[]).IsValueType);
+ IsFalse(typeof(int[]).IsValueType);
+ IsFalse(typeof(int[,,]).IsValueType);
+ IsFalse(typeof(IEnumerable<int>).IsValueType);
+ IsFalse(typeof(Action<int>).IsValueType);
+ IsTrue (typeof(GenericStruct<int>).IsValueType);
+ IsTrue (typeof(GenericStruct<string>).IsValueType);
+ IsTrue (typeof(GenericStruct<string>).IsValueType);
+ IsTrue (typeof(KeyValuePair<int, string>).IsValueType);
+ IsTrue (typeof(KeyValuePair<Program, string>).IsValueType);
+ IsTrue (typeof(SimpleEnum).IsValueType);
+ IsTrue (typeof(void).IsValueType);
+ IsFalse(typeof(ValueType).IsValueType);
+ IsFalse(typeof(List<>).IsValueType);
+ IsFalse(typeof(IDictionary<,>).IsValueType);
+ IsTrue (typeof(Vector128<>).IsValueType);
+ IsTrue (typeof(Vector128<byte>).IsValueType);
+
+ // Test __Canon
+ IsFalse(IsValueType<IEnumerable<int>>());
+ IsFalse(IsValueType<IEnumerable<string>>());
+ IsFalse(IsValueType<IEnumerable<IDisposable>>());
+ IsFalse(IsValueType<IDictionary<int, string>>());
+ IsFalse(IsValueType<IDictionary<IConvertible, IComparer<int>>>());
+ IsFalse(IsValueType<Dictionary<int, int>>());
+ IsFalse(IsValueType<Dictionary<string, IEnumerable>>());
+
+ // Test `x.GetType().IsX`
+ IsTrue (IsValueType<int>(42));
+ IsTrue (IsValueType<int?>(new Nullable<int>(42)));
+ IsTrue (IsValueType<decimal>(42M));
+ IsFalse(IsValueType<string>("42"));
+ IsFalse(IsValueType<object>(new object()));
+ IsFalse(IsValueType<IEnumerable<int>>(new int[10]));
+ IsFalse(IsValueType<Action<int>>(_ => { }));
+ IsTrue (IsValueType<GenericStruct<int>>(default));
+ IsTrue (IsValueType<GenericStruct<string>>(default));
+ IsTrue (IsValueType(SimpleEnum.B));
+ IsTrue (IsValueType(CreateDynamic1()));
+ IsFalse(IsValueType(CreateDynamic2()));
+
+ IsTrue (IsValueTypeObj(42));
+ IsTrue (IsValueTypeObj(new Nullable<int>(42)));
+ IsTrue (IsValueTypeObj(42M));
+ IsFalse(IsValueTypeObj("42"));
+ IsFalse(IsValueTypeObj(new object()));
+ IsFalse(IsValueTypeObj(new int[10]));
+ IsFalse(IsValueTypeObj((Action<int>)(_ => { })));
+ IsTrue (IsValueTypeObj(new GenericStruct<int>()));
+ IsTrue (IsValueTypeObj(new GenericStruct<string>()));
+ IsTrue (IsValueTypeObj(SimpleEnum.B));
+ IsTrue (IsValueTypeObj(CreateDynamic1()));
+ IsFalse(IsValueTypeObj(CreateDynamic2()));
+
+ IsTrue (IsValueTypeRef(ref _varInt));
+ IsTrue (IsValueTypeRef(ref _varNullableInt));
+ IsTrue (IsValueTypeRef(ref _varDecimal));
+ IsFalse(IsValueTypeRef(ref _varString));
+ IsFalse(IsValueTypeRef(ref _varObject));
+ IsFalse(IsValueTypeRef(ref _varArrayOfInt));
+ IsFalse(IsValueTypeRef(ref _varAction));
+ IsTrue (IsValueTypeRef(ref _varGenericStructInt));
+ IsTrue (IsValueTypeRef(ref _varGenericStructStr));
+ IsTrue (IsValueTypeRef(ref _varEnum));
+
+ ThrowsNRE(() => { IsValueType(_varNullableIntNull); });
+ ThrowsNRE(() => { IsValueType(_varStringNull); });
+ ThrowsNRE(() => { IsValueTypeRef(ref _varNullableIntNull); });
+ ThrowsNRE(() => { IsValueTypeRef(ref _varStringNull); });
+
+ return 100 + _errors;
+ }
+
+ private static int _varInt = 42;
+ private static int? _varNullableInt = 42;
+ private static decimal _varDecimal = 42M;
+ private static string _varString = "42";
+ private static object _varObject = new object();
+ private static int[] _varArrayOfInt = new int[10];
+ private static Action<int> _varAction = _ => { };
+ private static GenericStruct<int> _varGenericStructInt = new GenericStruct<int> { field = 42 };
+ private static GenericStruct<string> _varGenericStructStr = new GenericStruct<string> { field = "42" };
+ private static SimpleEnum _varEnum = SimpleEnum.B;
+
+ private static int? _varNullableIntNull = null;
+ private static string _varStringNull = null;
+
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static bool IsValueType<T>() => typeof(T).IsValueType;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool IsValueType<T>(T val) => val.GetType().IsValueType;
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static bool IsValueTypeRef<T>(ref T val) => val.GetType().IsValueType;
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static bool IsValueTypeObj(object val) => val.GetType().IsValueType;
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static dynamic CreateDynamic1() => 42;
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static dynamic CreateDynamic2() => new { Name = "Test" };
+
+
+ static void IsTrue(bool expression, [CallerLineNumber] int line = 0)
+ {
+ if (!expression)
+ {
+ Console.WriteLine($"Line {line}: test failed (expected: true).");
+ _errors++;
+ }
+ }
+
+ static void IsFalse(bool expression, [CallerLineNumber] int line = 0)
+ {
+ if (expression)
+ {
+ Console.WriteLine($"Line {line}: test failed (expected: false).");
+ _errors++;
+ }
+ }
+
+ static void ThrowsNRE(Action action, [CallerLineNumber] int line = 0)
+ {
+ try
+ {
+ action();
+ }
+ catch (NullReferenceException)
+ {
+ return;
+ }
+ catch (Exception exc)
+ {
+ Console.WriteLine($"Line {line}: {exc}");
+ }
+ Console.WriteLine($"Line {line}: test failed (expected: NullReferenceException)");
+ }
+}
+
+public struct GenericStruct<T>
+{
+ public T field;
+}
+
+public enum SimpleEnum
+{
+ A,B,C
+}
\ No newline at end of file
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+.assembly extern mscorlib { }
+
+.assembly TypeIntrinsicsTests
+{
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
+}
+
+.class private auto ansi beforefieldinit Test
+ extends [mscorlib]System.Object
+{
+ .method private hidebysig static int32 Main() cil managed
+ {
+ .entrypoint
+ .maxstack 1
+ // it's not currently possible to produce `ldtoken string&` in C#
+ ldtoken [System.Runtime]System.String&
+ call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
+ call instance bool [System.Runtime]System.Type::get_IsValueType()
+ brtrue FAILED
+
+ ldtoken [System.Runtime]System.Int16&
+ call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
+ call instance bool [System.Runtime]System.Type::get_IsValueType()
+ brfalse.s FAILED
+
+ ldtoken [System.Runtime]System.Object&
+ call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
+ call instance bool [System.Runtime]System.Type::get_IsValueType()
+ brtrue.s FAILED
+
+ ldtoken [System.Runtime]System.Decimal&
+ call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
+ call instance bool [System.Runtime]System.Type::get_IsValueType()
+ brfalse.s FAILED
+
+ ldc.i4.s 100
+ ret
+
+FAILED:
+ ldc.i4.s 42
+ ret
+ }
+}
\ No newline at end of file