Skip to content

Commit 74e5e8f

Browse files
authored
Optimize box + isinst + unbox.any as nop (#1817)
Extend box pattern match to cover `box + isinst + unbox.any` .
1 parent fbf2e6a commit 74e5e8f

File tree

3 files changed

+289
-1
lines changed

3 files changed

+289
-1
lines changed

src/coreclr/src/jit/importer.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5951,13 +5951,13 @@ int Compiler::impBoxPatternMatch(CORINFO_RESOLVED_TOKEN* pResolvedToken, const B
59515951
break;
59525952

59535953
case CEE_ISINST:
5954-
// box + isinst + br_true/false
59555954
if (codeAddr + 1 + sizeof(mdToken) + 1 <= codeEndp)
59565955
{
59575956
const BYTE* nextCodeAddr = codeAddr + 1 + sizeof(mdToken);
59585957

59595958
switch (nextCodeAddr[0])
59605959
{
5960+
// box + isinst + br_true/false
59615961
case CEE_BRTRUE:
59625962
case CEE_BRTRUE_S:
59635963
case CEE_BRFALSE:
@@ -5990,6 +5990,34 @@ int Compiler::impBoxPatternMatch(CORINFO_RESOLVED_TOKEN* pResolvedToken, const B
59905990
}
59915991
}
59925992
}
5993+
break;
5994+
5995+
// box + isinst + unbox.any
5996+
case CEE_UNBOX_ANY:
5997+
if ((nextCodeAddr + 1 + sizeof(mdToken)) <= codeEndp)
5998+
{
5999+
// See if the resolved tokens in box, isinst and unbox.any describe types that are equal.
6000+
CORINFO_RESOLVED_TOKEN isinstResolvedToken = {};
6001+
impResolveToken(codeAddr + 1, &isinstResolvedToken, CORINFO_TOKENKIND_Class);
6002+
6003+
if (info.compCompHnd->compareTypesForEquality(isinstResolvedToken.hClass,
6004+
pResolvedToken->hClass) ==
6005+
TypeCompareState::Must)
6006+
{
6007+
CORINFO_RESOLVED_TOKEN unboxResolvedToken = {};
6008+
impResolveToken(nextCodeAddr + 1, &unboxResolvedToken, CORINFO_TOKENKIND_Class);
6009+
6010+
// If so, box + isinst + unbox.any is a nop.
6011+
if (info.compCompHnd->compareTypesForEquality(unboxResolvedToken.hClass,
6012+
pResolvedToken->hClass) ==
6013+
TypeCompareState::Must)
6014+
{
6015+
JITDUMP("\n Importing BOX; ISINST, UNBOX.ANY as NOP\n");
6016+
return 2 + sizeof(mdToken) * 2;
6017+
}
6018+
}
6019+
}
6020+
break;
59936021
}
59946022
}
59956023
break;
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Runtime.CompilerServices;
8+
9+
public static class Tests
10+
{
11+
private static int returnCode = 100;
12+
13+
[MethodImpl(MethodImplOptions.NoInlining)]
14+
public static int BoxIsInstUnbox1<T>(T t) => t is int n ? n : -1;
15+
16+
[MethodImpl(MethodImplOptions.NoInlining)]
17+
public static int BoxIsInstUnbox2<T>(T t) => t is string n ? n.Length : -1;
18+
19+
[MethodImpl(MethodImplOptions.NoInlining)]
20+
public static int BoxIsInstUnbox3<T>(T t) => t is Struct1<int> n ? n.a : -1;
21+
22+
[MethodImpl(MethodImplOptions.NoInlining)]
23+
public static int BoxIsInstUnbox4<T>(T t) => t is Struct1<IDisposable> n ? n.GetHashCode() : -1;
24+
25+
[MethodImpl(MethodImplOptions.NoInlining)]
26+
public static int BoxIsInstUnbox5<T>(T t) => t is Class1<int> n ? n.a : -1;
27+
28+
[MethodImpl(MethodImplOptions.NoInlining)]
29+
public static int BoxIsInstUnbox6<T>(T t) => t is RefBase n ? n.a : -1;
30+
31+
[MethodImpl(MethodImplOptions.NoInlining)]
32+
public static int BoxIsInstUnbox7<T>(T t) => t is object[] n ? n.Length : -1;
33+
34+
public static void Expect(this int actual, int expected, [CallerLineNumber] int line = 0)
35+
{
36+
if (expected != actual)
37+
{
38+
Console.WriteLine($"{actual} != {expected}, line {line}.");
39+
returnCode++;
40+
}
41+
}
42+
43+
public static int Main()
44+
{
45+
BoxIsInstUnbox1<int>(1).Expect(1);
46+
BoxIsInstUnbox1<uint>(1).Expect(-1);
47+
BoxIsInstUnbox1<byte>(1).Expect(-1);
48+
BoxIsInstUnbox1<long>(1).Expect(-1);
49+
BoxIsInstUnbox1<decimal>(1).Expect(-1);
50+
BoxIsInstUnbox1<int?>(1).Expect(1);
51+
BoxIsInstUnbox1<int?>(null).Expect(-1);
52+
BoxIsInstUnbox1<uint?>(1).Expect(-1);
53+
BoxIsInstUnbox1<string>("1").Expect(-1);
54+
BoxIsInstUnbox1<string>(1.ToString()).Expect(-1);
55+
BoxIsInstUnbox1<string>(null).Expect(-1);
56+
BoxIsInstUnbox1<Struct1<int>>(new Struct1<int> { a = 1 }).Expect(-1);
57+
BoxIsInstUnbox1<Struct1<int>?>(new Struct1<int> { a = 1 }).Expect(-1);
58+
BoxIsInstUnbox1<Struct1<uint>>(new Struct1<uint> { a = 1 }).Expect(-1);
59+
BoxIsInstUnbox1<Struct2<IDisposable>>(new Struct2<IDisposable>()).Expect(-1);
60+
BoxIsInstUnbox1<Class1<int>>(new Class1<int> { a = 1 }).Expect(-1);
61+
BoxIsInstUnbox1<Class1<uint>>(new Class1<uint> { a = 1 }).Expect(-1);
62+
BoxIsInstUnbox1<Class1<string>>(new Class1<string> { a = 1 }).Expect(-1);
63+
BoxIsInstUnbox1<Class1<int>>(new Class1<int> { a = 1 }).Expect(-1);
64+
BoxIsInstUnbox1<Class1<uint>>(new Class1<uint> { a = 1 }).Expect(-1);
65+
BoxIsInstUnbox1<Class1<string>>(new Class1<string> { a = 1 }).Expect(-1);
66+
BoxIsInstUnbox1<Class1<IDisposable>>(new Class1<IDisposable> { a = 1 }).Expect(-1);
67+
BoxIsInstUnbox1<string[]>(new string[1]).Expect(-1);
68+
BoxIsInstUnbox1<object[]>(new string[1]).Expect(-1);
69+
BoxIsInstUnbox1<IEnumerable<object>>(new string[1]).Expect(-1);
70+
71+
BoxIsInstUnbox2<int>(1).Expect(-1);
72+
BoxIsInstUnbox2<uint>(1).Expect(-1);
73+
BoxIsInstUnbox2<byte>(1).Expect(-1);
74+
BoxIsInstUnbox2<long>(1).Expect(-1);
75+
BoxIsInstUnbox2<decimal>(1).Expect(-1);
76+
BoxIsInstUnbox2<int?>(1).Expect(-1);
77+
BoxIsInstUnbox2<int?>(null).Expect(-1);
78+
BoxIsInstUnbox2<uint?>(1).Expect(-1);
79+
BoxIsInstUnbox2<string>("1").Expect(1);
80+
BoxIsInstUnbox2<string>(1.ToString()).Expect(1);
81+
BoxIsInstUnbox2<string>(null).Expect(-1);
82+
BoxIsInstUnbox2<Struct1<int>>(new Struct1<int> { a = 1 }).Expect(-1);
83+
BoxIsInstUnbox2<Struct1<int>?>(new Struct1<int> { a = 1 }).Expect(-1);
84+
BoxIsInstUnbox2<Struct1<uint>>(new Struct1<uint> { a = 1 }).Expect(-1);
85+
BoxIsInstUnbox2<Struct2<IDisposable>>(new Struct2<IDisposable>()).Expect(-1);
86+
BoxIsInstUnbox2<Class1<int>>(new Class1<int> { a = 1 }).Expect(-1);
87+
BoxIsInstUnbox2<Class1<uint>>(new Class1<uint> { a = 1 }).Expect(-1);
88+
BoxIsInstUnbox2<Class1<string>>(new Class1<string> { a = 1 }).Expect(-1);
89+
BoxIsInstUnbox2<Class1<int>>(new Class1<int> { a = 1 }).Expect(-1);
90+
BoxIsInstUnbox2<Class1<uint>>(new Class1<uint> { a = 1 }).Expect(-1);
91+
BoxIsInstUnbox2<Class1<string>>(new Class1<string> { a = 1 }).Expect(-1);
92+
BoxIsInstUnbox2<Class1<IDisposable>>(new Class1<IDisposable> { a = 1 }).Expect(-1);
93+
BoxIsInstUnbox2<string[]>(new string[1]).Expect(-1);
94+
BoxIsInstUnbox2<object[]>(new string[1]).Expect(-1);
95+
BoxIsInstUnbox2<IEnumerable<object>>(new string[1]).Expect(-1);
96+
97+
BoxIsInstUnbox3<int>(1).Expect(-1);
98+
BoxIsInstUnbox3<uint>(1).Expect(-1);
99+
BoxIsInstUnbox3<byte>(1).Expect(-1);
100+
BoxIsInstUnbox3<long>(1).Expect(-1);
101+
BoxIsInstUnbox3<decimal>(1).Expect(-1);
102+
BoxIsInstUnbox3<int?>(1).Expect(-1);
103+
BoxIsInstUnbox3<int?>(null).Expect(-1);
104+
BoxIsInstUnbox3<uint?>(1).Expect(-1);
105+
BoxIsInstUnbox3<string>("1").Expect(-1);
106+
BoxIsInstUnbox3<string>(1.ToString()).Expect(-1);
107+
BoxIsInstUnbox3<string>(null).Expect(-1);
108+
BoxIsInstUnbox3<Struct1<int>>(new Struct1<int> { a = 1 }).Expect(1);
109+
BoxIsInstUnbox3<Struct1<int>?>(new Struct1<int> { a = 1 }).Expect(1);
110+
BoxIsInstUnbox3<Struct1<uint>>(new Struct1<uint> { a = 1 }).Expect(-1);
111+
BoxIsInstUnbox3<Struct2<IDisposable>>(new Struct2<IDisposable>()).Expect(-1);
112+
BoxIsInstUnbox3<Class1<int>>(new Class1<int> { a = 1 }).Expect(-1);
113+
BoxIsInstUnbox3<Class1<uint>>(new Class1<uint> { a = 1 }).Expect(-1);
114+
BoxIsInstUnbox3<Class1<string>>(new Class1<string> { a = 1 }).Expect(-1);
115+
BoxIsInstUnbox3<Class1<int>>(new Class1<int> { a = 1 }).Expect(-1);
116+
BoxIsInstUnbox3<Class1<uint>>(new Class1<uint> { a = 1 }).Expect(-1);
117+
BoxIsInstUnbox3<Class1<string>>(new Class1<string> { a = 1 }).Expect(-1);
118+
BoxIsInstUnbox3<Class1<IDisposable>>(new Class1<IDisposable> { a = 1 }).Expect(-1);
119+
BoxIsInstUnbox3<string[]>(new string[1]).Expect(-1);
120+
BoxIsInstUnbox3<object[]>(new string[1]).Expect(-1);
121+
BoxIsInstUnbox3<IEnumerable<object>>(new string[1]).Expect(-1);
122+
123+
BoxIsInstUnbox4<int>(1).Expect(-1);
124+
BoxIsInstUnbox4<uint>(1).Expect(-1);
125+
BoxIsInstUnbox4<byte>(1).Expect(-1);
126+
BoxIsInstUnbox4<long>(1).Expect(-1);
127+
BoxIsInstUnbox4<decimal>(1).Expect(-1);
128+
BoxIsInstUnbox4<int?>(1).Expect(-1);
129+
BoxIsInstUnbox4<int?>(null).Expect(-1);
130+
BoxIsInstUnbox4<uint?>(1).Expect(-1);
131+
BoxIsInstUnbox4<string>("1").Expect(-1);
132+
BoxIsInstUnbox4<string>(1.ToString()).Expect(-1);
133+
BoxIsInstUnbox4<string>(null).Expect(-1);
134+
BoxIsInstUnbox4<Struct1<int>>(new Struct1<int> { a = 1 }).Expect(-1);
135+
BoxIsInstUnbox4<Struct1<int>?>(new Struct1<int> { a = 1 }).Expect(-1);
136+
BoxIsInstUnbox4<Struct1<uint>>(new Struct1<uint> { a = 1 }).Expect(-1);
137+
BoxIsInstUnbox4<Struct2<IDisposable>>(new Struct2<IDisposable>()).Expect(-1);
138+
BoxIsInstUnbox4<Class1<int>>(new Class1<int> { a = 1 }).Expect(-1);
139+
BoxIsInstUnbox4<Class1<uint>>(new Class1<uint> { a = 1 }).Expect(-1);
140+
BoxIsInstUnbox4<Class1<string>>(new Class1<string> { a = 1 }).Expect(-1);
141+
BoxIsInstUnbox4<Class1<int>>(new Class1<int> { a = 1 }).Expect(-1);
142+
BoxIsInstUnbox4<Class1<uint>>(new Class1<uint> { a = 1 }).Expect(-1);
143+
BoxIsInstUnbox4<Class1<string>>(new Class1<string> { a = 1 }).Expect(-1);
144+
BoxIsInstUnbox4<Class1<IDisposable>>(new Class1<IDisposable> { a = 1 }).Expect(-1);
145+
BoxIsInstUnbox4<string[]>(new string[1]).Expect(-1);
146+
BoxIsInstUnbox4<object[]>(new string[1]).Expect(-1);
147+
BoxIsInstUnbox4<IEnumerable<object>>(new string[1]).Expect(-1);
148+
149+
BoxIsInstUnbox5<int>(1).Expect(-1);
150+
BoxIsInstUnbox5<uint>(1).Expect(-1);
151+
BoxIsInstUnbox5<byte>(1).Expect(-1);
152+
BoxIsInstUnbox5<long>(1).Expect(-1);
153+
BoxIsInstUnbox5<decimal>(1).Expect(-1);
154+
BoxIsInstUnbox5<int?>(1).Expect(-1);
155+
BoxIsInstUnbox5<int?>(null).Expect(-1);
156+
BoxIsInstUnbox5<uint?>(1).Expect(-1);
157+
BoxIsInstUnbox5<string>("1").Expect(-1);
158+
BoxIsInstUnbox5<string>(1.ToString()).Expect(-1);
159+
BoxIsInstUnbox5<string>(null).Expect(-1);
160+
BoxIsInstUnbox5<Struct1<int>>(new Struct1<int> { a = 1 }).Expect(-1);
161+
BoxIsInstUnbox5<Struct1<int>?>(new Struct1<int> { a = 1 }).Expect(-1);
162+
BoxIsInstUnbox5<Struct1<uint>>(new Struct1<uint> { a = 1 }).Expect(-1);
163+
BoxIsInstUnbox5<Struct2<IDisposable>>(new Struct2<IDisposable>()).Expect(-1);
164+
BoxIsInstUnbox5<Class1<int>>(new Class1<int> { a = 1 }).Expect(1);
165+
BoxIsInstUnbox5<Class1<uint>>(new Class1<uint> { a = 1 }).Expect(-1);
166+
BoxIsInstUnbox5<Class1<string>>(new Class1<string> { a = 1 }).Expect(-1);
167+
BoxIsInstUnbox5<Class1<int>>(new Class1<int> { a = 1 }).Expect(1);
168+
BoxIsInstUnbox5<Class1<uint>>(new Class1<uint> { a = 1 }).Expect(-1);
169+
BoxIsInstUnbox5<Class1<string>>(new Class1<string> { a = 1 }).Expect(-1);
170+
BoxIsInstUnbox5<Class1<IDisposable>>(new Class1<IDisposable> { a = 1 }).Expect(-1);
171+
BoxIsInstUnbox5<string[]>(new string[1]).Expect(-1);
172+
BoxIsInstUnbox5<object[]>(new string[1]).Expect(-1);
173+
BoxIsInstUnbox5<IEnumerable<object>>(new string[1]).Expect(-1);
174+
175+
BoxIsInstUnbox6<int>(1).Expect(-1);
176+
BoxIsInstUnbox6<uint>(1).Expect(-1);
177+
BoxIsInstUnbox6<byte>(1).Expect(-1);
178+
BoxIsInstUnbox6<long>(1).Expect(-1);
179+
BoxIsInstUnbox6<decimal>(1).Expect(-1);
180+
BoxIsInstUnbox6<int?>(1).Expect(-1);
181+
BoxIsInstUnbox6<int?>(null).Expect(-1);
182+
BoxIsInstUnbox6<uint?>(1).Expect(-1);
183+
BoxIsInstUnbox6<string>("1").Expect(-1);
184+
BoxIsInstUnbox6<string>(1.ToString()).Expect(-1);
185+
BoxIsInstUnbox6<string>(null).Expect(-1);
186+
BoxIsInstUnbox6<Struct1<int>>(new Struct1<int> { a = 1 }).Expect(-1);
187+
BoxIsInstUnbox6<Struct1<int>?>(new Struct1<int> { a = 1 }).Expect(-1);
188+
BoxIsInstUnbox6<Struct1<uint>>(new Struct1<uint> { a = 1 }).Expect(-1);
189+
BoxIsInstUnbox6<Struct2<IDisposable>>(new Struct2<IDisposable>()).Expect(-1);
190+
BoxIsInstUnbox6<Class1<int>>(new Class1<int> { a = 1 }).Expect(1);
191+
BoxIsInstUnbox6<Class1<uint>>(new Class1<uint> { a = 1 }).Expect(1);
192+
BoxIsInstUnbox6<Class1<string>>(new Class1<string> { a = 1 }).Expect(1);
193+
BoxIsInstUnbox6<Class1<int>>(new Class1<int> { a = 1 }).Expect(1);
194+
BoxIsInstUnbox6<Class1<uint>>(new Class1<uint> { a = 1 }).Expect(1);
195+
BoxIsInstUnbox6<Class1<string>>(new Class1<string> { a = 1 }).Expect(1);
196+
BoxIsInstUnbox6<Class1<IDisposable>>(new Class1<IDisposable> { a = 1 }).Expect(1);
197+
BoxIsInstUnbox6<string[]>(new string[1]).Expect(-1);
198+
BoxIsInstUnbox6<object[]>(new string[1]).Expect(-1);
199+
BoxIsInstUnbox6<IEnumerable<object>>(new string[1]).Expect(-1);
200+
201+
BoxIsInstUnbox7<int>(1).Expect(-1);
202+
BoxIsInstUnbox7<uint>(1).Expect(-1);
203+
BoxIsInstUnbox7<byte>(1).Expect(-1);
204+
BoxIsInstUnbox7<long>(1).Expect(-1);
205+
BoxIsInstUnbox7<decimal>(1).Expect(-1);
206+
BoxIsInstUnbox7<int?>(1).Expect(-1);
207+
BoxIsInstUnbox7<int?>(null).Expect(-1);
208+
BoxIsInstUnbox7<uint?>(1).Expect(-1);
209+
BoxIsInstUnbox7<string>("1").Expect(-1);
210+
BoxIsInstUnbox7<string>(1.ToString()).Expect(-1);
211+
BoxIsInstUnbox7<string>(null).Expect(-1);
212+
BoxIsInstUnbox7<Struct1<int>>(new Struct1<int> { a = 1 }).Expect(-1);
213+
BoxIsInstUnbox7<Struct1<int>?>(new Struct1<int> { a = 1 }).Expect(-1);
214+
BoxIsInstUnbox7<Struct1<uint>>(new Struct1<uint> { a = 1 }).Expect(-1);
215+
BoxIsInstUnbox7<Struct2<IDisposable>>(new Struct2<IDisposable>()).Expect(-1);
216+
BoxIsInstUnbox7<Class1<int>>(new Class1<int> { a = 1 }).Expect(-1);
217+
BoxIsInstUnbox7<Class1<uint>>(new Class1<uint> { a = 1 }).Expect(-1);
218+
BoxIsInstUnbox7<Class1<string>>(new Class1<string> { a = 1 }).Expect(-1);
219+
BoxIsInstUnbox7<Class1<int>>(new Class1<int> { a = 1 }).Expect(-1);
220+
BoxIsInstUnbox7<Class1<uint>>(new Class1<uint> { a = 1 }).Expect(-1);
221+
BoxIsInstUnbox7<Class1<string>>(new Class1<string> { a = 1 }).Expect(-1);
222+
BoxIsInstUnbox7<Class1<IDisposable>>(new Class1<IDisposable> { a = 1 }).Expect(-1);
223+
BoxIsInstUnbox7<string[]>(new string[1]).Expect(1);
224+
BoxIsInstUnbox7<object[]>(new string[1]).Expect(1);
225+
BoxIsInstUnbox7<IEnumerable<object>>(new string[1]).Expect(1);
226+
227+
return returnCode;
228+
}
229+
}
230+
231+
public struct Struct1<T>
232+
{
233+
public T a;
234+
}
235+
236+
public struct Struct2<T>
237+
{
238+
public T a;
239+
}
240+
241+
public class RefBase : IDisposable
242+
{
243+
public int a;
244+
public void Dispose() { }
245+
}
246+
247+
public class Class1<T> : RefBase
248+
{
249+
public T b;
250+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<DebugType>None</DebugType>
5+
<Optimize>True</Optimize>
6+
</PropertyGroup>
7+
<ItemGroup>
8+
<Compile Include="box_isinst_unbox.cs" />
9+
</ItemGroup>
10+
</Project>

0 commit comments

Comments
 (0)