Skip to content

Commit d0b4ca6

Browse files
authored
JIT: Fix BCE regression (#109466)
1 parent db6cef1 commit d0b4ca6

File tree

7 files changed

+99
-11
lines changed

7 files changed

+99
-11
lines changed

src/coreclr/jit/compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3069,6 +3069,7 @@ class Compiler
30693069
GenTree* op2 = nullptr);
30703070

30713071
GenTreeIntCon* gtNewIconNode(ssize_t value, var_types type = TYP_INT);
3072+
GenTreeIntCon* gtNewIconNodeWithVN(Compiler* comp, ssize_t value, var_types type = TYP_INT);
30723073
GenTreeIntCon* gtNewIconNode(unsigned fieldOffset, FieldSeq* fieldSeq);
30733074
GenTreeIntCon* gtNewNull();
30743075
GenTreeIntCon* gtNewTrue();

src/coreclr/jit/gentree.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7630,6 +7630,14 @@ GenTreeIntCon* Compiler::gtNewIconNode(ssize_t value, var_types type)
76307630
return new (this, GT_CNS_INT) GenTreeIntCon(type, value);
76317631
}
76327632

7633+
GenTreeIntCon* Compiler::gtNewIconNodeWithVN(Compiler* comp, ssize_t value, var_types type)
7634+
{
7635+
assert(genActualType(type) == type);
7636+
GenTreeIntCon* cns = new (this, GT_CNS_INT) GenTreeIntCon(type, value);
7637+
comp->fgUpdateConstTreeValueNumber(cns);
7638+
return cns;
7639+
}
7640+
76337641
GenTreeIntCon* Compiler::gtNewIconNode(unsigned fieldOffset, FieldSeq* fieldSeq)
76347642
{
76357643
return new (this, GT_CNS_INT) GenTreeIntCon(TYP_I_IMPL, static_cast<ssize_t>(fieldOffset), fieldSeq);

src/coreclr/jit/morph.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8699,8 +8699,8 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA
86998699
if ((mulOrDiv->OperIs(GT_DIV) && (constVal != -1) && (constVal != 1)) ||
87008700
(mulOrDiv->OperIs(GT_MUL) && !mulOrDiv->gtOverflow()))
87018701
{
8702-
GenTree* newOp1 = op1op1; // a
8703-
GenTree* newOp2 = gtNewIconNode(-constVal, op1op2->TypeGet()); // -C
8702+
GenTree* newOp1 = op1op1; // a
8703+
GenTree* newOp2 = gtNewIconNodeWithVN(this, -constVal, op1op2->TypeGet()); // -C
87048704
mulOrDiv->gtOp1 = newOp1;
87058705
mulOrDiv->gtOp2 = newOp2;
87068706
mulOrDiv->SetVNsFromNode(tree);
@@ -10735,7 +10735,7 @@ GenTree* Compiler::fgOptimizeMultiply(GenTreeOp* mul)
1073510735
}
1073610736

1073710737
// change the multiplication into a smaller multiplication (by 3, 5 or 9) and a shift
10738-
op1 = gtNewOperNode(GT_MUL, mul->TypeGet(), op1, gtNewIconNode(factor, mul->TypeGet()));
10738+
op1 = gtNewOperNode(GT_MUL, mul->TypeGet(), op1, gtNewIconNodeWithVN(this, factor, mul->TypeGet()));
1073910739
mul->gtOp1 = op1;
1074010740
fgMorphTreeDone(op1);
1074110741

@@ -11731,7 +11731,7 @@ GenTree* Compiler::fgMorphUModToAndSub(GenTreeOp* tree)
1173111731
const var_types type = tree->TypeGet();
1173211732

1173311733
const size_t cnsValue = (static_cast<size_t>(tree->gtOp2->AsIntConCommon()->IntegralValue())) - 1;
11734-
GenTree* const newTree = gtNewOperNode(GT_AND, type, tree->gtOp1, gtNewIconNode(cnsValue, type));
11734+
GenTree* const newTree = gtNewOperNode(GT_AND, type, tree->gtOp1, gtNewIconNodeWithVN(this, cnsValue, type));
1173511735

1173611736
INDEBUG(newTree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
1173711737

src/coreclr/jit/rangecheck.cpp

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,31 +1019,55 @@ Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool
10191019
GenTree* op1 = binop->gtGetOp1();
10201020
GenTree* op2 = binop->gtGetOp2();
10211021

1022+
ValueNum op1VN = op1->gtVNPair.GetConservative();
1023+
ValueNum op2VN = op2->gtVNPair.GetConservative();
1024+
1025+
ValueNumStore* vnStore = m_pCompiler->vnStore;
1026+
1027+
bool op1IsCns = vnStore->IsVNConstant(op1VN);
1028+
bool op2IsCns = vnStore->IsVNConstant(op2VN);
1029+
1030+
if (binop->OperIsCommutative() && op1IsCns && !op2IsCns)
1031+
{
1032+
// Normalize constants to the right for commutative operators.
1033+
std::swap(op1, op2);
1034+
std::swap(op1VN, op2VN);
1035+
std::swap(op1IsCns, op2IsCns);
1036+
}
1037+
10221038
// Special cases for binops where op2 is a constant
10231039
if (binop->OperIs(GT_AND, GT_RSH, GT_LSH, GT_UMOD))
10241040
{
1025-
if (!op2->IsIntCnsFitsInI32())
1041+
if (!op2IsCns)
10261042
{
10271043
// only cns is supported for op2 at the moment for &,%,<<,>> operators
10281044
return Range(Limit::keUnknown);
10291045
}
10301046

1031-
int icon = -1;
1047+
ssize_t op2Cns = vnStore->CoercedConstantValue<ssize_t>(op2VN);
1048+
if (!FitsIn<int>(op2Cns))
1049+
{
1050+
return Range(Limit::keUnknown);
1051+
}
1052+
1053+
int op1op2Cns = 0;
1054+
int icon = -1;
10321055
if (binop->OperIs(GT_AND))
10331056
{
10341057
// x & cns -> [0..cns]
1035-
icon = static_cast<int>(op2->AsIntCon()->IconValue());
1058+
icon = static_cast<int>(op2Cns);
10361059
}
10371060
else if (binop->OperIs(GT_UMOD))
10381061
{
10391062
// x % cns -> [0..cns-1]
1040-
icon = static_cast<int>(op2->AsIntCon()->IconValue()) - 1;
1063+
icon = static_cast<int>(op2Cns) - 1;
10411064
}
1042-
else if (binop->OperIs(GT_RSH, GT_LSH) && op1->OperIs(GT_AND) && op1->AsOp()->gtGetOp2()->IsIntCnsFitsInI32())
1065+
else if (binop->OperIs(GT_RSH, GT_LSH) && op1->OperIs(GT_AND) &&
1066+
vnStore->IsVNIntegralConstant<int>(op1->AsOp()->gtGetOp2()->gtVNPair.GetConservative(), &op1op2Cns))
10431067
{
10441068
// (x & cns1) >> cns2 -> [0..cns1>>cns2]
1045-
int icon1 = static_cast<int>(op1->AsOp()->gtGetOp2()->AsIntCon()->IconValue());
1046-
int icon2 = static_cast<int>(op2->AsIntCon()->IconValue());
1069+
int icon1 = op1op2Cns;
1070+
int icon2 = static_cast<int>(op2Cns);
10471071
if ((icon1 >= 0) && (icon2 >= 0) && (icon2 < 32))
10481072
{
10491073
icon = binop->OperIs(GT_RSH) ? (icon1 >> icon2) : (icon1 << icon2);

src/coreclr/jit/valuenum.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,6 +1342,24 @@ class ValueNumStore
13421342
return ConstantValueInternal<T>(vn DEBUGARG(true));
13431343
}
13441344

1345+
template <typename T>
1346+
bool IsVNIntegralConstant(ValueNum vn, T* value)
1347+
{
1348+
if (!IsVNConstant(vn) || !varTypeIsIntegral(TypeOfVN(vn)))
1349+
{
1350+
*value = 0;
1351+
return false;
1352+
}
1353+
ssize_t val = CoercedConstantValue<ssize_t>(vn);
1354+
if (FitsIn<T>(val))
1355+
{
1356+
*value = static_cast<T>(val);
1357+
return true;
1358+
}
1359+
*value = 0;
1360+
return false;
1361+
}
1362+
13451363
CORINFO_OBJECT_HANDLE ConstantObjHandle(ValueNum vn)
13461364
{
13471365
assert(IsVNObjHandle(vn));
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
4+
using System;
5+
using System.Runtime.CompilerServices;
6+
using System.Threading;
7+
using Xunit;
8+
9+
public class Runtime_109365
10+
{
11+
[Fact]
12+
public static void TestEntryPoint()
13+
{
14+
var c = new Runtime_109365();
15+
for (var i = 0; i < 1000; i++) // triggers tier-1
16+
{
17+
c.Hash(i);
18+
Thread.Sleep(1);
19+
}
20+
}
21+
22+
private readonly static int[] _perm = [1,2,3,4];
23+
24+
[MethodImpl(MethodImplOptions.NoInlining)]
25+
public int Hash(int value)
26+
{
27+
return _perm[value & (_perm.Length - 1)];
28+
}
29+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<Optimize>True</Optimize>
4+
</PropertyGroup>
5+
<ItemGroup>
6+
<Compile Include="$(MSBuildProjectName).cs" />
7+
</ItemGroup>
8+
</Project>

0 commit comments

Comments
 (0)