From: Egor Bogatov Date: Fri, 27 Dec 2019 19:42:39 +0000 (+0300) Subject: Intrinsify typeof(T).IsValueType (#1157) X-Git-Tag: submit/tizen/20210909.063632~10602 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=55b1303ed0bce2e422f6291f9387e1c2ad41b2fd;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Intrinsify typeof(T).IsValueType (#1157) --- diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index a409192..866df11 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -4015,6 +4015,36 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, 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: @@ -4306,6 +4336,13 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) 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) diff --git a/src/coreclr/src/jit/namedintrinsiclist.h b/src/coreclr/src/jit/namedintrinsiclist.h index a9e4565..77af045 100644 --- a/src/coreclr/src/jit/namedintrinsiclist.h +++ b/src/coreclr/src/jit/namedintrinsiclist.h @@ -19,6 +19,7 @@ enum NamedIntrinsic : unsigned short NI_System_Collections_Generic_EqualityComparer_get_Default, NI_System_Buffers_Binary_BinaryPrimitives_ReverseEndianness, NI_System_GC_KeepAlive, + NI_System_Type_get_IsValueType, #ifdef FEATURE_HW_INTRINSICS NI_IsSupported_True, diff --git a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.cs b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.cs new file mode 100644 index 0000000..6dee554 --- /dev/null +++ b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.cs @@ -0,0 +1,177 @@ +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*).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).IsValueType); + IsFalse(typeof(Action).IsValueType); + IsTrue (typeof(GenericStruct).IsValueType); + IsTrue (typeof(GenericStruct).IsValueType); + IsTrue (typeof(GenericStruct).IsValueType); + IsTrue (typeof(KeyValuePair).IsValueType); + IsTrue (typeof(KeyValuePair).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).IsValueType); + + // Test __Canon + IsFalse(IsValueType>()); + IsFalse(IsValueType>()); + IsFalse(IsValueType>()); + IsFalse(IsValueType>()); + IsFalse(IsValueType>>()); + IsFalse(IsValueType>()); + IsFalse(IsValueType>()); + + // Test `x.GetType().IsX` + IsTrue (IsValueType(42)); + IsTrue (IsValueType(new Nullable(42))); + IsTrue (IsValueType(42M)); + IsFalse(IsValueType("42")); + IsFalse(IsValueType(new object())); + IsFalse(IsValueType>(new int[10])); + IsFalse(IsValueType>(_ => { })); + IsTrue (IsValueType>(default)); + IsTrue (IsValueType>(default)); + IsTrue (IsValueType(SimpleEnum.B)); + IsTrue (IsValueType(CreateDynamic1())); + IsFalse(IsValueType(CreateDynamic2())); + + IsTrue (IsValueTypeObj(42)); + IsTrue (IsValueTypeObj(new Nullable(42))); + IsTrue (IsValueTypeObj(42M)); + IsFalse(IsValueTypeObj("42")); + IsFalse(IsValueTypeObj(new object())); + IsFalse(IsValueTypeObj(new int[10])); + IsFalse(IsValueTypeObj((Action)(_ => { }))); + IsTrue (IsValueTypeObj(new GenericStruct())); + IsTrue (IsValueTypeObj(new GenericStruct())); + 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 _varAction = _ => { }; + private static GenericStruct _varGenericStructInt = new GenericStruct { field = 42 }; + private static GenericStruct _varGenericStructStr = new GenericStruct { 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() => typeof(T).IsValueType; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsValueType(T val) => val.GetType().IsValueType; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool IsValueTypeRef(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 +{ + public T field; +} + +public enum SimpleEnum +{ + A,B,C +} \ No newline at end of file diff --git a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_il.il b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_il.il new file mode 100644 index 0000000..aa9fee6 --- /dev/null +++ b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_il.il @@ -0,0 +1,47 @@ +// 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 diff --git a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_il.ilproj b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_il.ilproj new file mode 100644 index 0000000..4a2b605 --- /dev/null +++ b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_il.ilproj @@ -0,0 +1,13 @@ + + + Exe + 1 + + + PdbOnly + True + + + + + diff --git a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_r.csproj b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_r.csproj new file mode 100644 index 0000000..22afa8a --- /dev/null +++ b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_r.csproj @@ -0,0 +1,10 @@ + + + Exe + None + + + + + + diff --git a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_ro.csproj b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_ro.csproj new file mode 100644 index 0000000..b8ce948 --- /dev/null +++ b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_ro.csproj @@ -0,0 +1,10 @@ + + + Exe + None + True + + + + + diff --git a/src/libraries/System.Private.CoreLib/src/System/Type.cs b/src/libraries/System.Private.CoreLib/src/System/Type.cs index c525497..28a6c98 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Type.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Type.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Reflection; using System.Diagnostics; using System.Globalization; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace System @@ -107,7 +108,7 @@ namespace System protected virtual bool IsMarshalByRefImpl() => false; public bool IsPrimitive => IsPrimitiveImpl(); protected abstract bool IsPrimitiveImpl(); - public bool IsValueType => IsValueTypeImpl(); + public bool IsValueType { [Intrinsic] get => IsValueTypeImpl(); } protected virtual bool IsValueTypeImpl() => IsSubclassOf(typeof(ValueType)); public virtual bool IsSignatureType => false;