Skip to content

Commit 759e377

Browse files
authored
JIT: fix assert when there are mixed types in Enum.HasFlags optimization (dotnet#23902)
In some cases the pre-boxed nodes may differ in type. Bail if they don't have the same stack type, then compute the result using the stack type. Extended the hasflags test with a case that shows this issue. Closes #23847
1 parent bf20ae7 commit 759e377

File tree

3 files changed

+42
-9
lines changed

3 files changed

+42
-9
lines changed

src/jit/gentree.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12920,7 +12920,8 @@ GenTree* Compiler::gtOptimizeEnumHasFlag(GenTree* thisOp, GenTree* flagOp)
1292012920
return nullptr;
1292112921
}
1292212922

12923-
GenTree* flagVal = gtTryRemoveBoxUpstreamEffects(flagOp, BR_REMOVE_BUT_NOT_NARROW);
12923+
// Do likewise with flagOp.
12924+
GenTree* flagVal = gtTryRemoveBoxUpstreamEffects(flagOp, BR_DONT_REMOVE);
1292412925
if (flagVal == nullptr)
1292512926
{
1292612927
// Note we may fail here if the flag operand comes from
@@ -12929,19 +12930,29 @@ GenTree* Compiler::gtOptimizeEnumHasFlag(GenTree* thisOp, GenTree* flagOp)
1292912930
return nullptr;
1293012931
}
1293112932

12933+
// Only proceed when both box sources have the same actual type.
12934+
// (this rules out long/int mismatches)
12935+
if (genActualType(thisVal->TypeGet()) != genActualType(flagVal->TypeGet()))
12936+
{
12937+
JITDUMP("bailing, pre-boxed values have different types\n");
12938+
return nullptr;
12939+
}
12940+
1293212941
// Yes, both boxes can be cleaned up. Optimize.
1293312942
JITDUMP("Optimizing call to Enum.HasFlag\n");
1293412943

12935-
// Undo the boxing of thisOp and prepare to operate directly
12936-
// on the original enum values.
12944+
// Undo the boxing of the Ops and prepare to operate directly
12945+
// on the pre-boxed values.
1293712946
thisVal = gtTryRemoveBoxUpstreamEffects(thisOp, BR_REMOVE_BUT_NOT_NARROW);
12947+
flagVal = gtTryRemoveBoxUpstreamEffects(flagOp, BR_REMOVE_BUT_NOT_NARROW);
1293812948

12939-
// Our trial removal above should guarantee successful removal here.
12949+
// Our trial removals above should guarantee successful removals here.
1294012950
assert(thisVal != nullptr);
12951+
assert(flagVal != nullptr);
12952+
assert(genActualType(thisVal->TypeGet()) == genActualType(flagVal->TypeGet()));
1294112953

12942-
// We should have a consistent view of the type
12943-
var_types type = thisVal->TypeGet();
12944-
assert(type == flagVal->TypeGet());
12954+
// Type to use for optimized check
12955+
var_types type = genActualType(thisVal->TypeGet());
1294512956

1294612957
// The thisVal and flagVal trees come from earlier statements.
1294712958
//

tests/src/JIT/opt/Enum/hasflag.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// except for the case in the try/catch.
99

1010
using System;
11+
using System.Runtime.CompilerServices;
1112

1213
enum E
1314
{
@@ -40,6 +41,12 @@ class EClass
4041
public E e;
4142
}
4243

44+
class ShortHolder
45+
{
46+
public ShortHolder(short s) { v = s; }
47+
public short v;
48+
}
49+
4350
class P
4451
{
4552
static E[] ArrayOfE = { E.RED, E.BLUE };
@@ -81,6 +88,20 @@ public static bool ByrefG(ref G e1, G e2)
8188
return e1.HasFlag(e2);
8289
}
8390

91+
// Example from GitHub 23847
92+
[MethodImpl(MethodImplOptions.NoInlining)]
93+
public static bool GitHub23847(E e1, short s)
94+
{
95+
return GitHub23847Aux(s).HasFlag(e1);
96+
}
97+
98+
// Once this is inlined we end up with a short pre-boxed value
99+
public static E GitHub23847Aux(short s)
100+
{
101+
ShortHolder h = new ShortHolder(s);
102+
return (E)h.v;
103+
}
104+
84105
public static int Main()
85106
{
86107
E e1 = E.RED;
@@ -120,8 +141,10 @@ public static int Main()
120141
G g1 = G.RED;
121142
bool true9 = ByrefG(ref g1, G.RED);
122143

144+
bool false10 = GitHub23847(E.RED, 0x100);
145+
123146
bool[] trueResults = {true0, true1, true2, true3, true4, true5, true6, true7, true8, true9};
124-
bool[] falseResults = {false0, false1, false2, false3, false4, false5, false6, false7, false8, false9};
147+
bool[] falseResults = {false0, false1, false2, false3, false4, false5, false6, false7, false8, false9, false10};
125148

126149
bool resultOk = true;
127150

tests/src/JIT/opt/Enum/hasflag.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT .0\UITestExtensionPackages</ReferencePath>
1515
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
1616
<NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
17-
<CLRTestPriority>1</CLRTestPriority>
1817
</PropertyGroup>
1918
<!-- Default configurations to help VS understand the configurations -->
2019
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>

0 commit comments

Comments
 (0)