diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 2679a816a3f48b..5e475ae2b7d368 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -5904,13 +5904,13 @@ int Compiler::impBoxPatternMatch(CORINFO_RESOLVED_TOKEN* pResolvedToken, const B break; case CEE_ISINST: - // box + isinst + br_true/false if (codeAddr + 1 + sizeof(mdToken) + 1 <= codeEndp) { const BYTE* nextCodeAddr = codeAddr + 1 + sizeof(mdToken); switch (nextCodeAddr[0]) { + // box + isinst + br_true/false case CEE_BRTRUE: case CEE_BRTRUE_S: case CEE_BRFALSE: @@ -5943,6 +5943,34 @@ int Compiler::impBoxPatternMatch(CORINFO_RESOLVED_TOKEN* pResolvedToken, const B } } } + break; + + // box + isinst + unbox.any + case CEE_UNBOX_ANY: + if ((nextCodeAddr + 1 + sizeof(mdToken)) <= codeEndp) + { + // See if the resolved tokens in box, isinst and unbox.any describe types that are equal. + CORINFO_RESOLVED_TOKEN isinstResolvedToken = {}; + impResolveToken(codeAddr + 1, &isinstResolvedToken, CORINFO_TOKENKIND_Class); + + if (info.compCompHnd->compareTypesForEquality(isinstResolvedToken.hClass, + pResolvedToken->hClass) == + TypeCompareState::Must) + { + CORINFO_RESOLVED_TOKEN unboxResolvedToken = {}; + impResolveToken(nextCodeAddr + 1, &unboxResolvedToken, CORINFO_TOKENKIND_Class); + + // If so, box + isinst + unbox.any is a nop. + if (info.compCompHnd->compareTypesForEquality(unboxResolvedToken.hClass, + pResolvedToken->hClass) == + TypeCompareState::Must) + { + JITDUMP("\n Importing BOX; ISINST, UNBOX.ANY as NOP\n"); + return 2 + sizeof(mdToken) * 2; + } + } + } + break; } } break; diff --git a/src/coreclr/tests/src/JIT/Generics/Conversions/Boxing/box_isinst_unbox.cs b/src/coreclr/tests/src/JIT/Generics/Conversions/Boxing/box_isinst_unbox.cs new file mode 100644 index 00000000000000..49bb899f9d4a59 --- /dev/null +++ b/src/coreclr/tests/src/JIT/Generics/Conversions/Boxing/box_isinst_unbox.cs @@ -0,0 +1,250 @@ +// 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.Generic; +using System.Runtime.CompilerServices; + +public static class Tests +{ + private static int returnCode = 100; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int BoxIsInstUnbox1(T t) => t is int n ? n : -1; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int BoxIsInstUnbox2(T t) => t is string n ? n.Length : -1; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int BoxIsInstUnbox3(T t) => t is Struct1 n ? n.a : -1; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int BoxIsInstUnbox4(T t) => t is Struct1 n ? n.GetHashCode() : -1; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int BoxIsInstUnbox5(T t) => t is Class1 n ? n.a : -1; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int BoxIsInstUnbox6(T t) => t is RefBase n ? n.a : -1; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int BoxIsInstUnbox7(T t) => t is object[] n ? n.Length : -1; + + public static void Expect(this int actual, int expected, [CallerLineNumber] int line = 0) + { + if (expected != actual) + { + Console.WriteLine($"{actual} != {expected}, line {line}."); + returnCode++; + } + } + + public static int Main() + { + BoxIsInstUnbox1(1).Expect(1); + BoxIsInstUnbox1(1).Expect(-1); + BoxIsInstUnbox1(1).Expect(-1); + BoxIsInstUnbox1(1).Expect(-1); + BoxIsInstUnbox1(1).Expect(-1); + BoxIsInstUnbox1(1).Expect(1); + BoxIsInstUnbox1(null).Expect(-1); + BoxIsInstUnbox1(1).Expect(-1); + BoxIsInstUnbox1("1").Expect(-1); + BoxIsInstUnbox1(1.ToString()).Expect(-1); + BoxIsInstUnbox1(null).Expect(-1); + BoxIsInstUnbox1>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox1?>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox1>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox1>(new Struct2()).Expect(-1); + BoxIsInstUnbox1>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox1>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox1>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox1>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox1>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox1>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox1>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox1(new string[1]).Expect(-1); + BoxIsInstUnbox1(new string[1]).Expect(-1); + BoxIsInstUnbox1>(new string[1]).Expect(-1); + + BoxIsInstUnbox2(1).Expect(-1); + BoxIsInstUnbox2(1).Expect(-1); + BoxIsInstUnbox2(1).Expect(-1); + BoxIsInstUnbox2(1).Expect(-1); + BoxIsInstUnbox2(1).Expect(-1); + BoxIsInstUnbox2(1).Expect(-1); + BoxIsInstUnbox2(null).Expect(-1); + BoxIsInstUnbox2(1).Expect(-1); + BoxIsInstUnbox2("1").Expect(1); + BoxIsInstUnbox2(1.ToString()).Expect(1); + BoxIsInstUnbox2(null).Expect(-1); + BoxIsInstUnbox2>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox2?>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox2>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox2>(new Struct2()).Expect(-1); + BoxIsInstUnbox2>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox2>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox2>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox2>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox2>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox2>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox2>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox2(new string[1]).Expect(-1); + BoxIsInstUnbox2(new string[1]).Expect(-1); + BoxIsInstUnbox2>(new string[1]).Expect(-1); + + BoxIsInstUnbox3(1).Expect(-1); + BoxIsInstUnbox3(1).Expect(-1); + BoxIsInstUnbox3(1).Expect(-1); + BoxIsInstUnbox3(1).Expect(-1); + BoxIsInstUnbox3(1).Expect(-1); + BoxIsInstUnbox3(1).Expect(-1); + BoxIsInstUnbox3(null).Expect(-1); + BoxIsInstUnbox3(1).Expect(-1); + BoxIsInstUnbox3("1").Expect(-1); + BoxIsInstUnbox3(1.ToString()).Expect(-1); + BoxIsInstUnbox3(null).Expect(-1); + BoxIsInstUnbox3>(new Struct1 { a = 1 }).Expect(1); + BoxIsInstUnbox3?>(new Struct1 { a = 1 }).Expect(1); + BoxIsInstUnbox3>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox3>(new Struct2()).Expect(-1); + BoxIsInstUnbox3>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox3>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox3>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox3>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox3>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox3>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox3>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox3(new string[1]).Expect(-1); + BoxIsInstUnbox3(new string[1]).Expect(-1); + BoxIsInstUnbox3>(new string[1]).Expect(-1); + + BoxIsInstUnbox4(1).Expect(-1); + BoxIsInstUnbox4(1).Expect(-1); + BoxIsInstUnbox4(1).Expect(-1); + BoxIsInstUnbox4(1).Expect(-1); + BoxIsInstUnbox4(1).Expect(-1); + BoxIsInstUnbox4(1).Expect(-1); + BoxIsInstUnbox4(null).Expect(-1); + BoxIsInstUnbox4(1).Expect(-1); + BoxIsInstUnbox4("1").Expect(-1); + BoxIsInstUnbox4(1.ToString()).Expect(-1); + BoxIsInstUnbox4(null).Expect(-1); + BoxIsInstUnbox4>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox4?>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox4>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox4>(new Struct2()).Expect(-1); + BoxIsInstUnbox4>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox4>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox4>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox4>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox4>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox4>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox4>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox4(new string[1]).Expect(-1); + BoxIsInstUnbox4(new string[1]).Expect(-1); + BoxIsInstUnbox4>(new string[1]).Expect(-1); + + BoxIsInstUnbox5(1).Expect(-1); + BoxIsInstUnbox5(1).Expect(-1); + BoxIsInstUnbox5(1).Expect(-1); + BoxIsInstUnbox5(1).Expect(-1); + BoxIsInstUnbox5(1).Expect(-1); + BoxIsInstUnbox5(1).Expect(-1); + BoxIsInstUnbox5(null).Expect(-1); + BoxIsInstUnbox5(1).Expect(-1); + BoxIsInstUnbox5("1").Expect(-1); + BoxIsInstUnbox5(1.ToString()).Expect(-1); + BoxIsInstUnbox5(null).Expect(-1); + BoxIsInstUnbox5>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox5?>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox5>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox5>(new Struct2()).Expect(-1); + BoxIsInstUnbox5>(new Class1 { a = 1 }).Expect(1); + BoxIsInstUnbox5>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox5>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox5>(new Class1 { a = 1 }).Expect(1); + BoxIsInstUnbox5>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox5>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox5>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox5(new string[1]).Expect(-1); + BoxIsInstUnbox5(new string[1]).Expect(-1); + BoxIsInstUnbox5>(new string[1]).Expect(-1); + + BoxIsInstUnbox6(1).Expect(-1); + BoxIsInstUnbox6(1).Expect(-1); + BoxIsInstUnbox6(1).Expect(-1); + BoxIsInstUnbox6(1).Expect(-1); + BoxIsInstUnbox6(1).Expect(-1); + BoxIsInstUnbox6(1).Expect(-1); + BoxIsInstUnbox6(null).Expect(-1); + BoxIsInstUnbox6(1).Expect(-1); + BoxIsInstUnbox6("1").Expect(-1); + BoxIsInstUnbox6(1.ToString()).Expect(-1); + BoxIsInstUnbox6(null).Expect(-1); + BoxIsInstUnbox6>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox6?>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox6>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox6>(new Struct2()).Expect(-1); + BoxIsInstUnbox6>(new Class1 { a = 1 }).Expect(1); + BoxIsInstUnbox6>(new Class1 { a = 1 }).Expect(1); + BoxIsInstUnbox6>(new Class1 { a = 1 }).Expect(1); + BoxIsInstUnbox6>(new Class1 { a = 1 }).Expect(1); + BoxIsInstUnbox6>(new Class1 { a = 1 }).Expect(1); + BoxIsInstUnbox6>(new Class1 { a = 1 }).Expect(1); + BoxIsInstUnbox6>(new Class1 { a = 1 }).Expect(1); + BoxIsInstUnbox6(new string[1]).Expect(-1); + BoxIsInstUnbox6(new string[1]).Expect(-1); + BoxIsInstUnbox6>(new string[1]).Expect(-1); + + BoxIsInstUnbox7(1).Expect(-1); + BoxIsInstUnbox7(1).Expect(-1); + BoxIsInstUnbox7(1).Expect(-1); + BoxIsInstUnbox7(1).Expect(-1); + BoxIsInstUnbox7(1).Expect(-1); + BoxIsInstUnbox7(1).Expect(-1); + BoxIsInstUnbox7(null).Expect(-1); + BoxIsInstUnbox7(1).Expect(-1); + BoxIsInstUnbox7("1").Expect(-1); + BoxIsInstUnbox7(1.ToString()).Expect(-1); + BoxIsInstUnbox7(null).Expect(-1); + BoxIsInstUnbox7>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox7?>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox7>(new Struct1 { a = 1 }).Expect(-1); + BoxIsInstUnbox7>(new Struct2()).Expect(-1); + BoxIsInstUnbox7>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox7>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox7>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox7>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox7>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox7>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox7>(new Class1 { a = 1 }).Expect(-1); + BoxIsInstUnbox7(new string[1]).Expect(1); + BoxIsInstUnbox7(new string[1]).Expect(1); + BoxIsInstUnbox7>(new string[1]).Expect(1); + + return returnCode; + } +} + +public struct Struct1 +{ + public T a; +} + +public struct Struct2 +{ + public T a; +} + +public class RefBase : IDisposable +{ + public int a; + public void Dispose() { } +} + +public class Class1 : RefBase +{ + public T b; +} diff --git a/src/coreclr/tests/src/JIT/Generics/Conversions/Boxing/box_isinst_unbox.csproj b/src/coreclr/tests/src/JIT/Generics/Conversions/Boxing/box_isinst_unbox.csproj new file mode 100644 index 00000000000000..3b6e765928d5d3 --- /dev/null +++ b/src/coreclr/tests/src/JIT/Generics/Conversions/Boxing/box_isinst_unbox.csproj @@ -0,0 +1,10 @@ + + + Exe + None + True + + + + +