From 98183967509708249b061839017d69e8fee39ca3 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 28 Dec 2019 02:32:57 +0300 Subject: [PATCH 1/7] Optimize Type.IsAssignableFrom --- src/coreclr/src/jit/compiler.h | 2 +- src/coreclr/src/jit/importer.cpp | 46 +++- src/coreclr/src/jit/namedintrinsiclist.h | 1 + .../TypeIntrinsics.IsAssignableFrom.cs | 223 ++++++++++++++++++ .../src/JIT/Intrinsics/TypeIntrinsics.cs | 16 +- .../src/System/Type.Helpers.cs | 2 + 6 files changed, 281 insertions(+), 9 deletions(-) create mode 100644 src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index bfc7bba9dfe1c0..b1def3b1f6ede7 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -3614,7 +3614,7 @@ class Compiler CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig, - unsigned methodFlags, + unsigned& methodFlags, int memberRef, bool readonlyCall, bool tailCall, diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 866df11b1f9afc..0736539179b90b 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -3425,7 +3425,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig, - unsigned methodFlags, + unsigned& methodFlags, int memberRef, bool readonlyCall, bool tailCall, @@ -4015,6 +4015,46 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, break; } + case NI_System_Type_IsAssignableFrom: + { + // Optimize `typeof(TTo).IsAssignableFrom(TTFrom)` to true/false + GenTree* typeTo = impStackTop(1).val; + GenTree* typeFrom = impStackTop(0).val; + + if (typeTo->IsCall() && typeFrom->IsCall()) + { + // make sure both arguments are `typeof()` + CORINFO_METHOD_HANDLE hTypeof = eeFindHelper(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE); + if ((typeTo->AsCall()->gtCallMethHnd == hTypeof) && + (typeFrom->AsCall()->gtCallMethHnd == hTypeof)) + { + CORINFO_CLASS_HANDLE hClassTo = + gtGetHelperArgClassHandle(typeTo->AsCall()->gtCallArgs->GetNode()); + CORINFO_CLASS_HANDLE hClassFrom = + gtGetHelperArgClassHandle(typeFrom->AsCall()->gtCallArgs->GetNode()); + + if (hClassTo == NO_CLASS_HANDLE || hClassFrom == NO_CLASS_HANDLE) + { + break; + } + + TypeCompareState castResult = info.compCompHnd->compareTypesForCast(hClassFrom, hClassTo); + if (castResult == TypeCompareState::May) + { + // requires runtime check + // e.g. __Canon, COMObjects, Nullable + break; + } + + methodFlags &= ~CORINFO_FLG_VIRTUAL; + retNode = gtNewIconNode((castResult == TypeCompareState::Must) ? 1 : 0); + impPopStack(); // drop both CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE calls + impPopStack(); + } + } + break; + } + case NI_System_Type_get_IsValueType: { // Optimize @@ -4342,6 +4382,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { result = NI_System_Type_get_IsValueType; } + else if (strcmp(methodName, "IsAssignableFrom") == 0) + { + result = NI_System_Type_IsAssignableFrom; + } } } #if defined(_TARGET_XARCH_) // We currently only support BSWAP on x86 diff --git a/src/coreclr/src/jit/namedintrinsiclist.h b/src/coreclr/src/jit/namedintrinsiclist.h index 77af04583715b6..7be747a9b80073 100644 --- a/src/coreclr/src/jit/namedintrinsiclist.h +++ b/src/coreclr/src/jit/namedintrinsiclist.h @@ -20,6 +20,7 @@ enum NamedIntrinsic : unsigned short NI_System_Buffers_Binary_BinaryPrimitives_ReverseEndianness, NI_System_GC_KeepAlive, NI_System_Type_get_IsValueType, + NI_System_Type_IsAssignableFrom, #ifdef FEATURE_HW_INTRINSICS NI_IsSupported_True, diff --git a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs new file mode 100644 index 00000000000000..7a9e888b0c1761 --- /dev/null +++ b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs @@ -0,0 +1,223 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +public partial class Program +{ + public static void TestIsAssignableFrom() + { + // Primitive types + IsTrue (typeof(void).IsAssignableFrom(typeof(void))); + IsTrue (typeof(byte).IsAssignableFrom(typeof(byte))); + IsTrue (typeof(int).IsAssignableFrom(typeof(int))); + IsTrue (typeof(float).IsAssignableFrom(typeof(float))); + IsTrue (typeof(double).IsAssignableFrom(typeof(double))); + IsTrue (typeof(byte*).IsAssignableFrom(typeof(byte*))); + IsTrue (typeof(sbyte*).IsAssignableFrom(typeof(byte*))); + IsTrue (typeof(void*).IsAssignableFrom(typeof(void*))); + IsTrue (typeof(byte**).IsAssignableFrom(typeof(byte**))); + IsFalse(typeof(byte).IsAssignableFrom(typeof(sbyte))); + IsFalse(typeof(sbyte).IsAssignableFrom(typeof(byte))); + IsFalse(typeof(int).IsAssignableFrom(typeof(long))); + IsFalse(typeof(int).IsAssignableFrom(typeof(void))); + IsFalse(typeof(void).IsAssignableFrom(typeof(long))); + IsFalse(typeof(long).IsAssignableFrom(typeof(int))); + IsFalse(typeof(float).IsAssignableFrom(typeof(double))); + IsFalse(typeof(double).IsAssignableFrom(typeof(float))); + IsFalse(typeof(double).IsAssignableFrom(typeof(long))); + IsFalse(typeof(int).IsAssignableFrom(typeof(float))); + IsFalse(typeof(sbyte*).IsAssignableFrom(typeof(ulong*))); + IsFalse(typeof(sbyte*).IsAssignableFrom(typeof(void*))); + IsFalse(typeof(void*).IsAssignableFrom(typeof(ulong*))); + IsFalse(typeof(sbyte*).IsAssignableFrom(typeof(IntPtr))); + IsFalse(typeof(IntPtr).IsAssignableFrom(typeof(sbyte*))); + IsFalse(typeof(byte**).IsAssignableFrom(typeof(byte*))); + IsFalse(typeof(byte*).IsAssignableFrom(typeof(byte**))); + + // Nullable + IsTrue (typeof(int?).IsAssignableFrom(typeof(int))); + IsTrue (typeof(int?).IsAssignableFrom(typeof(int?))); + IsTrue (typeof(GenericStruct1?).IsAssignableFrom(typeof(GenericStruct1?))); + IsTrue (typeof(GenericStruct1?).IsAssignableFrom(typeof(GenericStruct1?))); + IsTrue (typeof(SimpleEnum_int?).IsAssignableFrom(typeof(SimpleEnum_int?))); + IsFalse(typeof(int).IsAssignableFrom(typeof(int?))); + IsFalse(typeof(uint?).IsAssignableFrom(typeof(int?))); + IsFalse(typeof(int?).IsAssignableFrom(typeof(uint?))); + IsFalse(typeof(SimpleEnum_uint?).IsAssignableFrom(typeof(SimpleEnum_int?))); + IsFalse(typeof(SimpleEnum_int?).IsAssignableFrom(typeof(SimpleEnum_uint?))); + IsFalse(typeof(GenericStruct1?).IsAssignableFrom(typeof(GenericStruct1?))); + + // Enums + IsTrue (typeof(SimpleEnum_int).IsAssignableFrom(typeof(SimpleEnum_int))); + IsTrue (typeof(SimpleEnum_int?).IsAssignableFrom(typeof(SimpleEnum_int?))); + IsTrue (typeof(SimpleEnum_uint).IsAssignableFrom(typeof(SimpleEnum_uint))); + IsTrue (typeof(SimpleEnum_byte).IsAssignableFrom(typeof(SimpleEnum_byte))); + IsTrue (typeof(ValueType).IsAssignableFrom(typeof(SimpleEnum_uint))); + IsFalse(typeof(SimpleEnum_int).IsAssignableFrom(typeof(SimpleEnum_uint))); + IsFalse(typeof(SimpleEnum_uint).IsAssignableFrom(typeof(SimpleEnum_int))); + IsFalse(typeof(SimpleEnum_uint).IsAssignableFrom(typeof(SimpleEnum_byte))); + IsFalse(typeof(SimpleEnum_byte).IsAssignableFrom(typeof(SimpleEnum_uint))); + IsFalse(typeof(SimpleEnum_byte).IsAssignableFrom(typeof(byte))); + IsFalse(typeof(byte).IsAssignableFrom(typeof(SimpleEnum_byte))); + IsFalse(typeof(SimpleEnum_int).IsAssignableFrom(typeof(int))); + IsFalse(typeof(int).IsAssignableFrom(typeof(SimpleEnum_int))); + IsFalse(typeof(SimpleEnum_uint).IsAssignableFrom(typeof(float))); + IsFalse(typeof(float).IsAssignableFrom(typeof(SimpleEnum_uint))); + IsFalse(typeof(SimpleEnum_uint).IsAssignableFrom(typeof(ValueType))); + + // Covariance/Contravariance + IsTrue (typeof(IEnumerable).IsAssignableFrom(typeof(List))); + IsTrue (typeof(IEnumerable).IsAssignableFrom(typeof(List))); + IsTrue (typeof(IEnumerable).IsAssignableFrom(typeof(IList))); + IsTrue (typeof(IEnumerable).IsAssignableFrom(typeof(IList))); + IsTrue (typeof(IEnumerable).IsAssignableFrom(typeof(IList))); + IsTrue (typeof(Action).IsAssignableFrom(typeof(Action))); + IsTrue (typeof(object[]).IsAssignableFrom(typeof(string[]))); + IsTrue (typeof(object[,]).IsAssignableFrom(typeof(string[,]))); + IsTrue (typeof(SimpleEnum_int[,]).IsAssignableFrom(typeof(SimpleEnum_uint[,]))); + IsFalse(typeof(string[,]).IsAssignableFrom(typeof(object[,]))); + IsFalse(typeof(object[,]).IsAssignableFrom(typeof(string[,,]))); + IsFalse(typeof(IDictionary).IsAssignableFrom(typeof(IDictionary))); + IsFalse(typeof(IDictionary).IsAssignableFrom(typeof(Dictionary))); + IsFalse(typeof(Action).IsAssignableFrom(typeof(Action))); + IsFalse(typeof(Action).IsAssignableFrom(typeof(Action))); + IsFalse(typeof(List).IsAssignableFrom(typeof(IEnumerable))); + IsFalse(typeof(Action).IsAssignableFrom(typeof(Action))); + IsFalse(typeof(List).IsAssignableFrom(typeof(IEnumerable))); + IsFalse(typeof(IList).IsAssignableFrom(typeof(IEnumerable))); + IsFalse(typeof(IList).IsAssignableFrom(typeof(IEnumerable))); + IsFalse(typeof(IList).IsAssignableFrom(typeof(IEnumerable))); + + // Misc + IsTrue (typeof(object).IsAssignableFrom(typeof(byte))); + IsTrue (typeof(object).IsAssignableFrom(typeof(int))); + IsTrue (typeof(object).IsAssignableFrom(typeof(float))); + IsTrue (typeof(object).IsAssignableFrom(typeof(SimpleEnum_uint))); + IsTrue (typeof(object).IsAssignableFrom(typeof(IDisposable))); + IsTrue (typeof(object).IsAssignableFrom(typeof(IDictionary))); + IsTrue (typeof(object).IsAssignableFrom(typeof(List))); + IsTrue (typeof(object).IsAssignableFrom(typeof(List<>))); + IsTrue (typeof(object).IsAssignableFrom(typeof(Action<>))); + IsTrue (typeof(object).IsAssignableFrom(typeof(Action))); + IsTrue (typeof(object).IsAssignableFrom(typeof(Vector128))); + IsTrue (typeof(object).IsAssignableFrom(typeof(Vector256))); + IsTrue (typeof(ClassA).IsAssignableFrom(typeof(ClassA))); + IsTrue (typeof(ClassA).IsAssignableFrom(typeof(ClassB))); + IsTrue (typeof(ClassA).IsAssignableFrom(typeof(ClassC))); + IsTrue (typeof(decimal).IsAssignableFrom(typeof(decimal))); + IsTrue (typeof(Struct1).IsAssignableFrom(typeof(Struct1))); + IsTrue (typeof(IDisposable).IsAssignableFrom(typeof(Struct3))); + IsTrue (typeof(Dictionary<,>).IsAssignableFrom(typeof(Dictionary<,>))); + IsTrue (typeof(IDictionary<,>).IsAssignableFrom(typeof(IDictionary<,>))); + IsTrue (typeof(GenericStruct1<>).IsAssignableFrom(typeof(GenericStruct1<>))); + IsTrue (typeof(GenericStruct1).IsAssignableFrom(typeof(GenericStruct1))); + IsTrue (typeof(GenericStruct1).IsAssignableFrom(typeof(GenericStruct1))); + IsFalse(typeof(byte).IsAssignableFrom(typeof(IDisposable))); + IsFalse(typeof(IDisposable).IsAssignableFrom(typeof(IEnumerable))); + IsFalse(typeof(IDictionary).IsAssignableFrom(typeof(IDictionary))); + IsFalse(typeof(List).IsAssignableFrom(typeof(IList))); + IsFalse(typeof(List<>).IsAssignableFrom(typeof(List))); + IsFalse(typeof(Action<>).IsAssignableFrom(typeof(Action))); + IsFalse(typeof(Action<>).IsAssignableFrom(typeof(Func))); + IsFalse(typeof(Action).IsAssignableFrom(typeof(CustomAction))); + IsFalse(typeof(Action).IsAssignableFrom(typeof(void))); + IsFalse(typeof(ClassB).IsAssignableFrom(typeof(ClassD))); + IsFalse(typeof(Dictionary<,>).IsAssignableFrom(typeof(Dictionary))); + IsFalse(typeof(GenericStruct1).IsAssignableFrom(typeof(GenericStruct1))); + IsFalse(typeof(Struct1).IsAssignableFrom(typeof(Struct2))); + IsFalse(typeof(GenericStruct1<>).IsAssignableFrom(typeof(GenericStruct2<>))); + IsFalse(typeof(GenericStruct1).IsAssignableFrom(typeof(GenericStruct2))); + IsFalse(typeof(object).IsAssignableFrom(typeof(byte*))); + IsFalse(typeof(object).IsAssignableFrom(typeof(byte**))); + IsFalse(typeof(Vector128).IsAssignableFrom(typeof(Vector128))); + IsFalse(typeof(Vector128).IsAssignableFrom(typeof(Vector128))); + IsFalse(typeof(Vector128).IsAssignableFrom(typeof(Vector128))); + IsFalse(typeof(Vector4).IsAssignableFrom(typeof(Vector128))); + IsFalse(typeof(Vector128).IsAssignableFrom(typeof(Vector4))); + IsFalse(typeof(Vector128).IsAssignableFrom(typeof(Vector))); + IsFalse(typeof(Vector256).IsAssignableFrom(typeof(Vector))); + + // System.__Canon + IsTrue (IsAssignableFrom, KeyValuePair>()); + IsTrue (IsAssignableFrom, KeyValuePair>()); + IsTrue (IsAssignableFrom, IDictionary>()); + IsTrue (IsAssignableFrom, IDictionary>()); + IsTrue (IsAssignableFrom, Dictionary>()); + IsTrue (IsAssignableFrom, Dictionary>()); + IsTrue (IsAssignableFrom, KeyValuePair>()); + IsTrue (IsAssignableFrom, IEnumerable>, KeyValuePair, IEnumerable>>()); + IsFalse(IsAssignableFrom, KeyValuePair>()); + IsFalse(IsAssignableFrom, KeyValuePair>()); + IsFalse(IsAssignableFrom, IDictionary>()); + IsFalse(IsAssignableFrom, IDictionary>()); + IsFalse(IsAssignableFrom, Dictionary>()); + IsFalse(IsAssignableFrom, Dictionary>()); + IsFalse(IsAssignableFrom, KeyValuePair>()); + IsFalse(IsAssignableFrom, IEnumerable>, KeyValuePair, IEnumerable>>()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool IsAssignableFrom() => typeof(TTo).IsAssignableFrom(typeof(TTFrom)); +} + +public struct Struct1 +{ + public int field1; +} + +public struct Struct2 +{ + public int field1; +} + +public struct Struct3 : IDisposable +{ + public int field1; + public void Dispose(){} +} + +public struct GenericStruct1 +{ + public T field; +} + +public struct GenericStruct2 +{ + public T field; +} + +public enum SimpleEnum_byte : byte +{ + A, B, C +} + +public enum SimpleEnum_int : int +{ + A,B,C +} + +public enum SimpleEnum_uint : uint +{ + D,E +} + +public class ClassA +{ +} + +public class ClassB : ClassA +{ +} + +public class ClassC : ClassB +{ +} + +public class ClassD : ClassA +{ +} + +public delegate void CustomAction(); \ No newline at end of file diff --git a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.cs b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.cs index 6dee554e2bf122..100ef1f314d68d 100644 --- a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.cs +++ b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; -public class Program +public partial class Program { private static int _errors = 0; @@ -93,6 +93,8 @@ public static int Main(string[] args) ThrowsNRE(() => { IsValueTypeRef(ref _varNullableIntNull); }); ThrowsNRE(() => { IsValueTypeRef(ref _varStringNull); }); + TestIsAssignableFrom(); + return 100 + _errors; } @@ -130,25 +132,25 @@ public static int Main(string[] args) private static dynamic CreateDynamic2() => new { Name = "Test" }; - static void IsTrue(bool expression, [CallerLineNumber] int line = 0) + static void IsTrue(bool expression, [CallerLineNumber] int line = 0, [CallerFilePath] string file = "") { if (!expression) { - Console.WriteLine($"Line {line}: test failed (expected: true)."); + Console.WriteLine($"{file}:L{line} test failed (expected: true)."); _errors++; } } - static void IsFalse(bool expression, [CallerLineNumber] int line = 0) + static void IsFalse(bool expression, [CallerLineNumber] int line = 0, [CallerFilePath] string file = "") { if (expression) { - Console.WriteLine($"Line {line}: test failed (expected: false)."); + Console.WriteLine($"{file}:L{line} test failed (expected: false)."); _errors++; } } - static void ThrowsNRE(Action action, [CallerLineNumber] int line = 0) + static void ThrowsNRE(Action action, [CallerLineNumber] int line = 0, [CallerFilePath] string file = "") { try { @@ -160,7 +162,7 @@ static void ThrowsNRE(Action action, [CallerLineNumber] int line = 0) } catch (Exception exc) { - Console.WriteLine($"Line {line}: {exc}"); + Console.WriteLine($"{file}:L{line} {exc}"); } Console.WriteLine($"Line {line}: test failed (expected: NullReferenceException)"); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Type.Helpers.cs b/src/libraries/System.Private.CoreLib/src/System/Type.Helpers.cs index 20e41d9c5bcc74..700f3b7391800d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Type.Helpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Type.Helpers.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Reflection; +using System.Runtime.CompilerServices; namespace System { @@ -330,6 +331,7 @@ public virtual bool IsSubclassOf(Type c) return false; } + [Intrinsic] public virtual bool IsAssignableFrom(Type? c) { if (c == null) From 4d15e44c377fed18f875d05d1dbc37783a0794a1 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 28 Dec 2019 03:06:47 +0300 Subject: [PATCH 2/7] Add TypeIntrinsics.IsAssignableFrom to TypeIntrinsics.csproj --- src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_r.csproj | 1 + src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_ro.csproj | 1 + 2 files changed, 2 insertions(+) diff --git a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_r.csproj b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_r.csproj index 22afa8a834949d..4e2edb234c81e0 100644 --- a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_r.csproj +++ b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_r.csproj @@ -6,5 +6,6 @@ + diff --git a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_ro.csproj b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_ro.csproj index b8ce948a144729..2e35b529e622a2 100644 --- a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_ro.csproj +++ b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics_ro.csproj @@ -6,5 +6,6 @@ + From a7c741dc27f645d2f7bb94a17aa817c62aea3cf4 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 28 Dec 2019 03:21:06 +0300 Subject: [PATCH 3/7] Formatting --- src/coreclr/src/jit/importer.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 0736539179b90b..635b78b99fc5c0 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -4025,8 +4025,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, { // make sure both arguments are `typeof()` CORINFO_METHOD_HANDLE hTypeof = eeFindHelper(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE); - if ((typeTo->AsCall()->gtCallMethHnd == hTypeof) && - (typeFrom->AsCall()->gtCallMethHnd == hTypeof)) + if ((typeTo->AsCall()->gtCallMethHnd == hTypeof) && (typeFrom->AsCall()->gtCallMethHnd == hTypeof)) { CORINFO_CLASS_HANDLE hClassTo = gtGetHelperArgClassHandle(typeTo->AsCall()->gtCallArgs->GetNode()); From 55fc28c58971c2196b42f52eec3ebde688978234 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 28 Dec 2019 15:17:42 +0300 Subject: [PATCH 4/7] Add License header --- .../src/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs | 4 ++++ src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs index 7a9e888b0c1761..ba748b8b7f218d 100644 --- a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs +++ b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs @@ -1,3 +1,7 @@ +// 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. + using System; using System.Collections; using System.Collections.Generic; diff --git a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.cs b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.cs index 100ef1f314d68d..70baf501cee790 100644 --- a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.cs +++ b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.cs @@ -1,3 +1,7 @@ +// 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. + using System; using System.Collections; using System.Collections.Generic; From 81efe869c164ef9d065e1dba9fde69292cb562d5 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 28 Dec 2019 15:19:06 +0300 Subject: [PATCH 5/7] Remove assert --- src/coreclr/src/jit/compiler.h | 2 +- src/coreclr/src/jit/importer.cpp | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index b1def3b1f6ede7..bfc7bba9dfe1c0 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -3614,7 +3614,7 @@ class Compiler CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig, - unsigned& methodFlags, + unsigned methodFlags, int memberRef, bool readonlyCall, bool tailCall, diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 635b78b99fc5c0..b70cb8832fbec4 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -3425,7 +3425,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig, - unsigned& methodFlags, + unsigned methodFlags, int memberRef, bool readonlyCall, bool tailCall, @@ -4045,7 +4045,6 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, break; } - methodFlags &= ~CORINFO_FLG_VIRTUAL; retNode = gtNewIconNode((castResult == TypeCompareState::Must) ? 1 : 0); impPopStack(); // drop both CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE calls impPopStack(); @@ -7613,9 +7612,6 @@ var_types Compiler::impImportCall(OPCODE opcode, if (call != nullptr) { - assert(!(mflags & CORINFO_FLG_VIRTUAL) || (mflags & CORINFO_FLG_FINAL) || - (clsFlags & CORINFO_FLG_FINAL)); - #ifdef FEATURE_READYTORUN_COMPILER if (call->OperGet() == GT_INTRINSIC) { From ce6088710bda0a22f3fe179b393ca229553fd91b Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 28 Dec 2019 17:35:00 +0300 Subject: [PATCH 6/7] fix "mustexpand assert" --- src/coreclr/src/jit/importer.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index b70cb8832fbec4..eaaabb1c9f424e 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -3448,8 +3448,8 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, if ((methodFlags & CORINFO_FLG_JIT_INTRINSIC) != 0) { - // The recursive calls to Jit intrinsics are must-expand by convention. - mustExpand = mustExpand || gtIsRecursiveCall(method); + // The recursive non-virtual calls to Jit intrinsics are must-expand by convention. + mustExpand = mustExpand || (gtIsRecursiveCall(method) && !(methodFlags & CORINFO_FLG_VIRTUAL)); if (intrinsicID == CORINFO_INTRINSIC_Illegal) { @@ -4017,7 +4017,12 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, case NI_System_Type_IsAssignableFrom: { - // Optimize `typeof(TTo).IsAssignableFrom(TTFrom)` to true/false + // Optimize patterns like: + // + // typeof(TTo).IsAssignableFrom(typeof(TTFrom)) + // valueTypeVar.GetType().IsAssignableFrom(typeof(TTFrom)) + // + // to true/false GenTree* typeTo = impStackTop(1).val; GenTree* typeFrom = impStackTop(0).val; From 78a0a3b146f43cfaf614a30f4cf5496c6c8974db Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 1 Feb 2020 22:27:27 +0300 Subject: [PATCH 7/7] Add tests for Arrays of vt --- .../TypeIntrinsics.IsAssignableFrom.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs index ba748b8b7f218d..65a25549695867 100644 --- a/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs +++ b/src/coreclr/tests/src/JIT/Intrinsics/TypeIntrinsics.IsAssignableFrom.cs @@ -95,6 +95,29 @@ public static void TestIsAssignableFrom() IsFalse(typeof(IList).IsAssignableFrom(typeof(IEnumerable))); IsFalse(typeof(IList).IsAssignableFrom(typeof(IEnumerable))); + // Arrays + IsTrue(typeof(byte[]).IsAssignableFrom(typeof(sbyte[]))); + IsTrue(typeof(sbyte[]).IsAssignableFrom(typeof(byte[]))); + IsTrue(typeof(short[]).IsAssignableFrom(typeof(ushort[]))); + IsTrue(typeof(ushort[]).IsAssignableFrom(typeof(short[]))); + IsTrue(typeof(int[]).IsAssignableFrom(typeof(uint[]))); + IsTrue(typeof(uint[]).IsAssignableFrom(typeof(int[]))); + IsTrue(typeof(long[]).IsAssignableFrom(typeof(ulong[]))); + IsTrue(typeof(ulong[]).IsAssignableFrom(typeof(long[]))); + IsTrue(typeof(long[,]).IsAssignableFrom(typeof(ulong[,]))); + IsTrue(typeof(ulong[,,]).IsAssignableFrom(typeof(long[,,]))); + IsTrue(typeof(Struct1[]).IsAssignableFrom(typeof(Struct1[]))); + IsFalse(typeof(int[]).IsAssignableFrom(typeof(byte[]))); + IsFalse(typeof(int[]).IsAssignableFrom(typeof(sbyte[]))); + IsFalse(typeof(int[]).IsAssignableFrom(typeof(short[]))); + IsFalse(typeof(int[]).IsAssignableFrom(typeof(ushort[]))); + IsFalse(typeof(int[]).IsAssignableFrom(typeof(float[]))); + IsFalse(typeof(int[]).IsAssignableFrom(typeof(double[]))); + IsFalse(typeof(long[]).IsAssignableFrom(typeof(double[]))); + IsFalse(typeof(Struct1[]).IsAssignableFrom(typeof(Struct2[]))); + IsFalse(typeof(Struct1[]).IsAssignableFrom(typeof(GenericStruct1[]))); + IsFalse(typeof(GenericStruct1[]).IsAssignableFrom(typeof(GenericStruct1[]))); + // Misc IsTrue (typeof(object).IsAssignableFrom(typeof(byte))); IsTrue (typeof(object).IsAssignableFrom(typeof(int)));