diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 8a2d862d1fe2c5..8dfb9d413b4b46 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4068,6 +4068,9 @@ class Compiler GenTree** clone, unsigned curLevel, Statement** pAfterStmt DEBUGARG(const char* reason)); + + CORINFO_CLASS_HANDLE impImportHandleFromStack(bool allowShared); + GenTree* impStoreStruct(GenTree* store, unsigned curLevel, Statement** pAfterStmt = nullptr, diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index e745bce1bd0379..82dfc863b2139b 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -2039,6 +2039,45 @@ GenTree* Compiler::impCloneExpr(GenTree* tree, return gtNewLclvNode(temp, type); } +//------------------------------------------------------------------------ +// impImportHandleFromStack: Imports the tree on the top of the stack +// and extracts the handle from it +// +// Arguments: +// allowShared - whether shared types are considered as valid handles +// +// Return Value: +// The extracted handle, or NO_CLASS_HANDLE if no handle could be extracted +// +CORINFO_CLASS_HANDLE Compiler::impImportHandleFromStack(bool allowShared) +{ + GenTree* tree = impStackTop().val; + bool isExact = false; + bool notNull = false; + CORINFO_CLASS_HANDLE typeHnd = gtGetClassHandle(tree, &isExact, ¬Null); + if ((typeHnd != NO_CLASS_HANDLE) && isExact) + { + assert((info.compCompHnd->getClassAttribs(typeHnd) & CORINFO_FLG_GENERIC_TYPE_VARIABLE) == 0); + if (!allowShared && ((info.compCompHnd->getClassAttribs(typeHnd) & CORINFO_FLG_SHAREDINST) != 0)) + { + return NO_CLASS_HANDLE; + } + + JITDUMP("Retuning a constant handle from imported tree\n"); + impPopStack(); + if (!notNull && fgAddrCouldBeNull(tree)) + { + impAppendTree(gtNewNullCheck(tree, compCurBB), CHECK_SPILL_ALL, impCurStmtDI); + } + else if ((tree->gtFlags & GTF_SIDE_EFFECT) != 0) + { + impAppendTree(gtUnusedValNode(tree), CHECK_SPILL_ALL, impCurStmtDI); + } + return typeHnd; + } + return NO_CLASS_HANDLE; +} + //------------------------------------------------------------------------ // impCreateDIWithCurrentStackInfo: Create a DebugInfo instance with the // specified IL offset and 'is call' bit, using the current stack to determine diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 7a304b11469eda..cb4f1d24f9ffbb 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -3561,6 +3561,18 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, } } + // Load handle directly for known types + if (retNode == nullptr) + { + CORINFO_CLASS_HANDLE typeHnd = impImportHandleFromStack(false); + if (typeHnd != NO_CLASS_HANDLE) + { + JITDUMP("Optimizing object.GetType() with known type to typeof\n"); + GenTree* handle = gtNewIconEmbClsHndNode(typeHnd); + retNode = gtNewHelperCallNode(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE, TYP_REF, handle); + } + } + #ifdef DEBUG if (retNode != nullptr) { diff --git a/src/tests/readytorun/tests/generics.cs b/src/tests/readytorun/tests/generics.cs index d4dc2acd79cc24..233b377a4f89a9 100644 --- a/src/tests/readytorun/tests/generics.cs +++ b/src/tests/readytorun/tests/generics.cs @@ -3073,13 +3073,17 @@ public static class Assert { public static bool HasAssertFired; + [MethodImplAttribute(MethodImplOptions.NoInlining)] public static void AreEqual(Object actual, Object expected) { - if (!(actual == null && expected == null) && !actual.Equals(expected)) + if (ReferenceEquals(expected, actual)) + return; + + if (ReferenceEquals(expected, null) || !expected.Equals(actual)) { Console.WriteLine("Not equal!"); - Console.WriteLine("actual = " + actual.ToString()); - Console.WriteLine("expected = " + expected.ToString()); + Console.WriteLine("expected = " + expected?.ToString()); + Console.WriteLine("actual = " + actual?.ToString()); HasAssertFired = true; } } diff --git a/src/tests/readytorun/tests/main.cs b/src/tests/readytorun/tests/main.cs index 04314860d48ca0..3fe94bb692eeb2 100644 --- a/src/tests/readytorun/tests/main.cs +++ b/src/tests/readytorun/tests/main.cs @@ -278,7 +278,9 @@ static void TestChangingHFAStruct() [MethodImplAttribute(MethodImplOptions.NoInlining)] static void TestGetType() { - new MyClass().GetType().ToString(); + NoInline(new MyClass()).GetType().ToString(); + [MethodImplAttribute(MethodImplOptions.NoInlining)] + static object NoInline(object o) => o; } [MethodImplAttribute(MethodImplOptions.NoInlining)] @@ -393,17 +395,17 @@ static void GenericLdtokenFieldsTest() string expectedDllField4 = "System.Collections.Generic.KeyValuePair`2[???,System.Int32] MyGeneric`2[???,???]::m_Field4".Replace("???", instArg.ToString()); string expectedDllField5 = "System.Int32 MyGeneric`2[???,???]::m_Field5".Replace("???", instArg.ToString()); - Assert.AreEqual(expectedField1, FieldFullName(getter.GetGenT_Field1())); - Assert.AreEqual(expectedField2, FieldFullName(getter.GetGenT_Field2())); - Assert.AreEqual(expectedField3, FieldFullName(getter.GetGenT_Field3())); - Assert.AreEqual(expectedField4, FieldFullName(getter.GetGenT_Field4())); - Assert.AreEqual(expectedField5, FieldFullName(getter.GetGenT_Field5())); - - Assert.AreEqual(expectedDllField1, FieldFullName(getter.GetGenDllT_Field1())); - Assert.AreEqual(expectedDllField2, FieldFullName(getter.GetGenDllT_Field2())); - Assert.AreEqual(expectedDllField3, FieldFullName(getter.GetGenDllT_Field3())); - Assert.AreEqual(expectedDllField4, FieldFullName(getter.GetGenDllT_Field4())); - Assert.AreEqual(expectedDllField5, FieldFullName(getter.GetGenDllT_Field5())); + Assert.AreEqual(FieldFullName(getter.GetGenT_Field1()), expectedField1); + Assert.AreEqual(FieldFullName(getter.GetGenT_Field2()), expectedField2); + Assert.AreEqual(FieldFullName(getter.GetGenT_Field3()), expectedField3); + Assert.AreEqual(FieldFullName(getter.GetGenT_Field4()), expectedField4); + Assert.AreEqual(FieldFullName(getter.GetGenT_Field5()), expectedField5); + + Assert.AreEqual(FieldFullName(getter.GetGenDllT_Field1()), expectedDllField1); + Assert.AreEqual(FieldFullName(getter.GetGenDllT_Field2()), expectedDllField2); + Assert.AreEqual(FieldFullName(getter.GetGenDllT_Field3()), expectedDllField3); + Assert.AreEqual(FieldFullName(getter.GetGenDllT_Field4()), expectedDllField4); + Assert.AreEqual(FieldFullName(getter.GetGenDllT_Field5()), expectedDllField5); } } @@ -438,7 +440,7 @@ static void TestILBodyChange() { int actualMethodCallResult = (int)typeof(ILInliningTest).GetMethod("TestDifferentIntValue").Invoke(null, new object[]{}); Console.WriteLine(actualMethodCallResult); - Assert.AreEqual(ILInliningTest.TestDifferentIntValue(), actualMethodCallResult); + Assert.AreEqual(actualMethodCallResult, ILInliningTest.TestDifferentIntValue()); } static void RunAllTests() diff --git a/src/tests/readytorun/tests/newarray.cs b/src/tests/readytorun/tests/newarray.cs index 6aa4409458cdd6..da175102b59ae7 100644 --- a/src/tests/readytorun/tests/newarray.cs +++ b/src/tests/readytorun/tests/newarray.cs @@ -112,13 +112,17 @@ public static class Assert { public static bool HasAssertFired; + [MethodImplAttribute(MethodImplOptions.NoInlining)] public static void AreEqual(Object actual, Object expected) { - if (!(actual == null && expected == null) && !actual.Equals(expected)) + if (ReferenceEquals(expected, actual)) + return; + + if (ReferenceEquals(expected, null) || !expected.Equals(actual)) { Console.WriteLine("Not equal!"); - Console.WriteLine("actual = " + actual.ToString()); - Console.WriteLine("expected = " + expected.ToString()); + Console.WriteLine("expected = " + expected?.ToString()); + Console.WriteLine("actual = " + actual?.ToString()); HasAssertFired = true; } } diff --git a/src/tests/readytorun/tests/test.cs b/src/tests/readytorun/tests/test.cs index 73e4dfb7a2befc..c434101755b9be 100644 --- a/src/tests/readytorun/tests/test.cs +++ b/src/tests/readytorun/tests/test.cs @@ -14,13 +14,17 @@ public static class Assert { public static bool HasAssertFired; + [MethodImplAttribute(MethodImplOptions.NoInlining)] public static void AreEqual(Object actual, Object expected) { - if (!(actual == null && expected == null) && !actual.Equals(expected)) + if (ReferenceEquals(expected, actual)) + return; + + if (ReferenceEquals(expected, null) || !expected.Equals(actual)) { Console.WriteLine("Not equal!"); - Console.WriteLine("actual = " + actual.ToString()); - Console.WriteLine("expected = " + expected.ToString()); + Console.WriteLine("expected = " + expected?.ToString()); + Console.WriteLine("actual = " + actual?.ToString()); HasAssertFired = true; } } @@ -623,8 +627,6 @@ static void TestMovedGenericVirtualMethod() Assert.AreEqual(o.ChangedToVirtual(), typeof(List).ToString()); } - - [MethodImplAttribute(MethodImplOptions.NoInlining)] static void TestMovedGenericVirtualMethodOnNullReference() { @@ -729,7 +731,9 @@ static void TestChangingHFAStruct() [MethodImplAttribute(MethodImplOptions.NoInlining)] static void TestGetType() { - new MyClass().GetType().ToString(); + NoInline(new MyClass()).GetType().ToString(); + [MethodImplAttribute(MethodImplOptions.NoInlining)] + static object NoInline(object o) => o; } [MethodImplAttribute(MethodImplOptions.NoInlining)] @@ -848,17 +852,17 @@ static void TestGenericLdtokenFields() string expectedDllField4 = "System.Collections.Generic.KeyValuePair`2[???,System.Int32] MyGeneric`2[???,???]::m_Field4".Replace("???", instArg.ToString()); string expectedDllField5 = "System.Int32 MyGeneric`2[???,???]::m_Field5".Replace("???", instArg.ToString()); - Assert.AreEqual(expectedField1, FieldFullName(getter.GetGenT_Field1())); - Assert.AreEqual(expectedField2, FieldFullName(getter.GetGenT_Field2())); - Assert.AreEqual(expectedField3, FieldFullName(getter.GetGenT_Field3())); - Assert.AreEqual(expectedField4, FieldFullName(getter.GetGenT_Field4())); - Assert.AreEqual(expectedField5, FieldFullName(getter.GetGenT_Field5())); - - Assert.AreEqual(expectedDllField1, FieldFullName(getter.GetGenDllT_Field1())); - Assert.AreEqual(expectedDllField2, FieldFullName(getter.GetGenDllT_Field2())); - Assert.AreEqual(expectedDllField3, FieldFullName(getter.GetGenDllT_Field3())); - Assert.AreEqual(expectedDllField4, FieldFullName(getter.GetGenDllT_Field4())); - Assert.AreEqual(expectedDllField5, FieldFullName(getter.GetGenDllT_Field5())); + Assert.AreEqual(FieldFullName(getter.GetGenT_Field1()), expectedField1); + Assert.AreEqual(FieldFullName(getter.GetGenT_Field2()), expectedField2); + Assert.AreEqual(FieldFullName(getter.GetGenT_Field3()), expectedField3); + Assert.AreEqual(FieldFullName(getter.GetGenT_Field4()), expectedField4); + Assert.AreEqual(FieldFullName(getter.GetGenT_Field5()), expectedField5); + + Assert.AreEqual(FieldFullName(getter.GetGenDllT_Field1()), expectedDllField1); + Assert.AreEqual(FieldFullName(getter.GetGenDllT_Field2()), expectedDllField2); + Assert.AreEqual(FieldFullName(getter.GetGenDllT_Field3()), expectedDllField3); + Assert.AreEqual(FieldFullName(getter.GetGenDllT_Field4()), expectedDllField4); + Assert.AreEqual(FieldFullName(getter.GetGenDllT_Field5()), expectedDllField5); } } @@ -888,7 +892,7 @@ private static void ValidateTestHasCrossModuleImplementation(string testName, Li found = true; } Console.WriteLine($"Found:{found}"); - Assert.AreEqual(expectedToBePresent, found); + Assert.AreEqual(found, expectedToBePresent); } public static void RunAllTests(Assembly assembly) @@ -1012,4 +1016,4 @@ public static void RunAllTests(Assembly assembly) } static int s; -} \ No newline at end of file +}