Skip to content

Commit 88dbd11

Browse files
authored
Unify O2K_CHECKED_BOUND_ADD_CNS and O2K_VN (#128165)
I decided that it makes no sense to have these separate. So now the semantics of `O2K_VN_ADD_CNS`: 1) Decomposed "X+CNS" into `op2.GetVN()` being X and CNS being `op2.GetCNS()`. Exactly like `O2K_CHECKED_BOUND_ADD_CNS` used to be. The decomposition allows us to avoid calling GetVNFunc in the hot loop in MergeEdgeAssertions since we usually are more interested in that X rather than the whole ADD's VN. 2) `O2K_VN_ADD_CNS` alone doesn't imply that anything is non-negative and no callers assume that (we used to have a bug where IsVNCheckedBound was assumed to be unconditionally never-negative). There is `GetOp2().IsVNNeverNegative()` which only exists because bound check node actually produce two assertions we can't spawn today because trees may only have 1 assertion. I'm thinking of solving this by introducing an aggregate (or just allow trees to have 2 assertions), or just add a fake COMMA 3) I intentionally tried to make this almost 0 diffs, but there are just a few improvements I couldn't shut (they're not related to the non-negativitiy).
1 parent 8df6a1f commit 88dbd11

3 files changed

Lines changed: 68 additions & 89 deletions

File tree

src/coreclr/jit/assertionprop.cpp

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -899,13 +899,8 @@ void Compiler::optPrintAssertion(const AssertionDsc& curAssertion, AssertionInde
899899
IntegralRange::Print(curAssertion.GetOp2().GetIntegralRange());
900900
break;
901901

902-
case O2K_CHECKED_BOUND_ADD_CNS:
903-
printf("(Checked_Bnd_BinOp " FMT_VN " + %d)", curAssertion.GetOp2().GetCheckedBound(),
904-
curAssertion.GetOp2().GetCheckedBoundConstant());
905-
break;
906-
907-
case O2K_VN:
908-
printf("VN " FMT_VN "", curAssertion.GetOp2().GetVN());
902+
case O2K_VN_ADD_CNS:
903+
printf("(VN_ADD_CNS " FMT_VN " + %d)", curAssertion.GetOp2().GetVN(), curAssertion.GetOp2().GetCns());
909904
break;
910905

911906
default:
@@ -1413,9 +1408,9 @@ AssertionIndex Compiler::optAddAssertion(const AssertionDsc& newAssertion)
14131408
bool mayHaveDuplicates =
14141409
optAssertionHasAssertionsForVN(newAssertion.GetOp1().GetVN(), /* addIfNotFound */ canAddNewAssertions);
14151410
// We need to register op2.vn too, even if we know for sure there are no duplicates
1416-
if (newAssertion.GetOp2().KindIs(O2K_CHECKED_BOUND_ADD_CNS))
1411+
if (newAssertion.GetOp2().KindIs(O2K_VN_ADD_CNS))
14171412
{
1418-
mayHaveDuplicates |= optAssertionHasAssertionsForVN(newAssertion.GetOp2().GetCheckedBound(),
1413+
mayHaveDuplicates |= optAssertionHasAssertionsForVN(newAssertion.GetOp2().GetVN(),
14191414
/* addIfNotFound */ canAddNewAssertions);
14201415

14211416
// Additionally, check for the pattern of "VN + const == checkedBndVN" and register "VN" as well.
@@ -1425,13 +1420,6 @@ AssertionIndex Compiler::optAddAssertion(const AssertionDsc& newAssertion)
14251420
mayHaveDuplicates |= optAssertionHasAssertionsForVN(addOpVN, /* addIfNotFound */ canAddNewAssertions);
14261421
}
14271422
}
1428-
else if (newAssertion.GetOp2().KindIs(O2K_VN))
1429-
{
1430-
// For VN <relop> VN assertions, register op2's VN too so consumers can find
1431-
// the assertion when iterating from the op2 side.
1432-
mayHaveDuplicates |= optAssertionHasAssertionsForVN(newAssertion.GetOp2().GetVN(),
1433-
/* addIfNotFound */ canAddNewAssertions);
1434-
}
14351423

14361424
if (mayHaveDuplicates)
14371425
{
@@ -1561,7 +1549,7 @@ void Compiler::optDebugCheckAssertion(const AssertionDsc& assertion) const
15611549
assert(optLocalAssertionProp);
15621550
break;
15631551

1564-
case O2K_VN:
1552+
case O2K_VN_ADD_CNS:
15651553
assert(!optLocalAssertionProp);
15661554
assert(assertion.GetOp1().KindIs(O1K_VN));
15671555
assert(assertion.IsRelop());
@@ -1650,8 +1638,7 @@ void Compiler::optCreateComplementaryAssertion(AssertionIndex assertionIndex)
16501638
AssertionDsc reversed = candidateAssertion.Reverse();
16511639
optMapComplementary(optAddAssertion(reversed), assertionIndex);
16521640
}
1653-
else if (candidateAssertion.KindIs(OAK_LT_UN, OAK_LE_UN) &&
1654-
candidateAssertion.GetOp2().KindIs(O2K_CHECKED_BOUND_ADD_CNS))
1641+
else if (candidateAssertion.KindIs(OAK_LT_UN, OAK_LE_UN) && candidateAssertion.GetOp2().KindIs(O2K_VN_ADD_CNS))
16551642
{
16561643
// Assertions such as "X > checkedBndVN" aren't very useful.
16571644
return;
@@ -4360,11 +4347,13 @@ GenTree* Compiler::optAssertionPropGlobal_RelOp(ASSERT_VALARG_TP assertions,
43604347
// Look for a relop-like assertion that matches the current relop exactly.
43614348
// Example: currentTree is "X >= Y" and we have an assertion "X >= Y" (or its inverse "X < Y").
43624349
//
4350+
// For O2K_VN_ADD_CNS the assertion stores op2 as "vn + cns".
4351+
// - When cns == 0, the stored VN equals the original op2 VN, so direct match works.
4352+
// - When cns != 0, we'd need to assemble ADD(vn, cns) at the VN level; skip for now.
4353+
//
43634354
if (curAssertion.IsRelop() && (curAssertion.GetOp1().GetVN() == op1VN) &&
4364-
// O2K_CHECKED_BOUND_ADD_CNS means op2 is decomposed into op2.vn being checked bound
4365-
// and op2.cns being the constant offset. We assemble the original relop VN if we want.
4366-
// For now, just skip such assertions here.
4367-
!curAssertion.GetOp2().KindIs(O2K_CHECKED_BOUND_ADD_CNS) && (curAssertion.GetOp2().GetVN() == op2VN))
4355+
(!curAssertion.GetOp2().KindIs(O2K_VN_ADD_CNS) || (curAssertion.GetOp2().GetCns() == 0)) &&
4356+
(curAssertion.GetOp2().GetVN() == op2VN))
43684357
{
43694358
bool isUnsigned;
43704359
genTreeOps assertionOper = AssertionDsc::ToCompareOper(curAssertion.GetKind(), &isUnsigned);
@@ -5449,12 +5438,11 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree
54495438
continue;
54505439
}
54515440

5452-
assert(curAssertion.GetOp2().GetCheckedBoundConstant() == 0);
5453-
assert(curAssertion.GetOp2().IsCheckedBoundNeverNegative());
5441+
assert(curAssertion.GetOp2().GetCns() == 0);
5442+
assert(curAssertion.GetOp2().IsVNNeverNegative());
54545443

54555444
// Do we have a previous range check involving the same 'vnLen' upper bound?
5456-
if (curAssertion.GetOp2().GetCheckedBound() ==
5457-
vnStore->VNConservativeNormalValue(arrBndsChk->GetArrayLength()->gtVNPair))
5445+
if (curAssertion.GetOp2().GetVN() == optConservativeNormalVN(arrBndsChk->GetArrayLength()))
54585446
{
54595447
// Do we have the exact same lower bound 'vnIdx'?
54605448
// a[i] followed by a[i]

src/coreclr/jit/compiler.h

Lines changed: 30 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8192,14 +8192,10 @@ class Compiler
81928192
O2K_CONST_INT,
81938193
O2K_CONST_DOUBLE,
81948194

8195-
O2K_CHECKED_BOUND_ADD_CNS, // "checkedBndVN + cns" where op2.vn holds the "checkedBndVN"
8196-
// and op2.iconVal holds the "cns".
8197-
// "Checked bound" alone doesn't mean anything,
8198-
// nor it implies that it's never negative.
8195+
O2K_VN_ADD_CNS, // op2 represents an arbitrary VN (op2.GetVN()) plus a constant (op2.GetCns()).
81998196
O2K_ZEROOBJ,
82008197
O2K_SUBRANGE,
82018198
O2K_CONST_VEC,
8202-
O2K_VN, // op2 is an arbitrary value number (used for VN <relop> VN assertions in global prop).
82038199
};
82048200

82058201
struct AssertionDsc
@@ -8258,7 +8254,7 @@ class Compiler
82588254
private:
82598255
INDEBUG(const Compiler* m_compiler);
82608256
optOp2Kind m_kind;
8261-
bool m_checkedBoundIsNeverNegative; // only meaningful for O2K_CHECKED_BOUND_ADD_CNS kind
8257+
bool m_isVNNeverNegative; // only meaningful for O2K_VN_ADD_CNS kind
82628258
union
82638259
{
82648260
uint16_t m_encodedIconFlags; // encoded icon gtFlags; only meaningful for O2K_CONST_INT.
@@ -8345,35 +8341,26 @@ class Compiler
83458341
ValueNum GetVN() const
83468342
{
83478343
assert(!m_compiler->optLocalAssertionProp);
8348-
assert(KindIs(O2K_CONST_INT, O2K_CONST_DOUBLE, O2K_ZEROOBJ, O2K_CONST_VEC, O2K_VN));
8344+
assert(KindIs(O2K_CONST_INT, O2K_CONST_DOUBLE, O2K_ZEROOBJ, O2K_CONST_VEC, O2K_VN_ADD_CNS));
83498345
assert(m_vn != ValueNumStore::NoVN);
83508346
return m_vn;
83518347
}
83528348

8353-
// For "checkedBndVN + cns" form, return the "cns" part.
8354-
int GetCheckedBoundConstant() const
8349+
// For "vn + cns" form, return the "cns" part.
8350+
int GetCns() const
83558351
{
83568352
assert(!m_compiler->optLocalAssertionProp);
8357-
assert(KindIs(O2K_CHECKED_BOUND_ADD_CNS));
8353+
assert(KindIs(O2K_VN_ADD_CNS));
83588354
assert(FitsIn<int>(m_icon.m_iconVal));
83598355
return (int)m_icon.m_iconVal;
83608356
}
83618357

8362-
// For "checkedBndVN + cns" form, return the "checkedBndVN" part.
8363-
// We intentionally don't allow to use it via GetVN() to avoid confusion.
8364-
ValueNum GetCheckedBound() const
8358+
// For "vn + cns" form, true iff the JIT has proven the "vn" part to be non-negative.
8359+
bool IsVNNeverNegative() const
83658360
{
83668361
assert(!m_compiler->optLocalAssertionProp);
8367-
assert(KindIs(O2K_CHECKED_BOUND_ADD_CNS));
8368-
assert(m_vn != ValueNumStore::NoVN);
8369-
return m_vn;
8370-
}
8371-
8372-
bool IsCheckedBoundNeverNegative() const
8373-
{
8374-
assert(!m_compiler->optLocalAssertionProp);
8375-
assert(KindIs(O2K_CHECKED_BOUND_ADD_CNS));
8376-
return m_checkedBoundIsNeverNegative;
8362+
assert(KindIs(O2K_VN_ADD_CNS));
8363+
return m_isVNNeverNegative;
83778364
}
83788365

83798366
optOp2Kind GetKind() const
@@ -8500,10 +8487,10 @@ class Compiler
85008487

85018488
bool IsBoundsCheckNoThrow() const
85028489
{
8503-
// O1K_VN (idx) u< O2K_VN (len) where len is never negative.
8490+
// O1K_VN (idx) u< O2K_VN_ADD_CNS (len) where len is never negative.
85048491
// Effectively, it's "idx >= 0 && idx < len"
8505-
return GetOp1().KindIs(O1K_VN) && KindIs(OAK_LT_UN) && GetOp2().KindIs(O2K_CHECKED_BOUND_ADD_CNS) &&
8506-
(GetOp2().GetCheckedBoundConstant() == 0) && GetOp2().IsCheckedBoundNeverNegative();
8492+
return GetOp1().KindIs(O1K_VN) && KindIs(OAK_LT_UN) && GetOp2().KindIs(O2K_VN_ADD_CNS) &&
8493+
(GetOp2().GetCns() == 0) && GetOp2().IsVNNeverNegative();
85078494
}
85088495

85098496
// Convert VNFunc to optAssertionKind
@@ -8685,20 +8672,16 @@ class Compiler
86858672
case O2K_ZEROOBJ:
86868673
return true;
86878674

8688-
case O2K_CHECKED_BOUND_ADD_CNS:
8689-
return GetOp2().GetCheckedBound() == that.GetOp2().GetCheckedBound() &&
8690-
GetOp2().GetCheckedBoundConstant() == that.GetOp2().GetCheckedBoundConstant() &&
8691-
GetOp2().IsCheckedBoundNeverNegative() == that.GetOp2().IsCheckedBoundNeverNegative();
8675+
case O2K_VN_ADD_CNS:
8676+
return GetOp2().GetVN() == that.GetOp2().GetVN() && GetOp2().GetCns() == that.GetOp2().GetCns() &&
8677+
GetOp2().IsVNNeverNegative() == that.GetOp2().IsVNNeverNegative();
86928678

86938679
case O2K_LCLVAR_COPY:
86948680
return GetOp2().GetLclNum() == that.GetOp2().GetLclNum();
86958681

86968682
case O2K_SUBRANGE:
86978683
return GetOp2().GetIntegralRange().Equals(that.GetOp2().GetIntegralRange());
86988684

8699-
case O2K_VN:
8700-
return GetOp2().GetVN() == that.GetOp2().GetVN();
8701-
87028685
default:
87038686
assert(!"Unexpected value for GetOp2().m_kind in AssertionDsc.");
87048687
break;
@@ -8914,12 +8897,13 @@ class Compiler
89148897
dsc.m_assertionKind = OAK_LT_UN;
89158898
dsc.m_op1.m_kind = O1K_VN;
89168899
dsc.m_op1.m_vn = idxVN;
8917-
dsc.m_op2.m_kind = O2K_CHECKED_BOUND_ADD_CNS;
8900+
dsc.m_op2.m_kind = O2K_VN_ADD_CNS;
89188901
dsc.m_op2.m_vn = lenVN;
89198902

8920-
// Normally, "Checked bound" doesn't mean it's never negative, but in this particular case we know it is.
8921-
dsc.m_op2.m_checkedBoundIsNeverNegative = true;
8922-
dsc.m_op2.m_icon.m_iconVal = 0;
8903+
// Normally, the VN of an O2K_VN_ADD_CNS assertion isn't proven non-negative, but
8904+
// in this particular case we know it is (it's a length).
8905+
dsc.m_op2.m_isVNNeverNegative = true;
8906+
dsc.m_op2.m_icon.m_iconVal = 0;
89238907
return dsc;
89248908
}
89258909

@@ -8934,7 +8918,7 @@ class Compiler
89348918
dsc.m_assertionKind = FromVNFunc(relop);
89358919
dsc.m_op1.m_kind = O1K_VN;
89368920
dsc.m_op1.m_vn = op1VN;
8937-
dsc.m_op2.m_kind = O2K_CHECKED_BOUND_ADD_CNS;
8921+
dsc.m_op2.m_kind = O2K_VN_ADD_CNS;
89388922
dsc.m_op2.m_vn = checkedBndVN;
89398923
dsc.m_op2.m_icon.m_iconVal = cns;
89408924
return dsc;
@@ -8968,12 +8952,14 @@ class Compiler
89688952
assert(op2VN != ValueNumStore::NoVN);
89698953
assert(op1VN != op2VN);
89708954

8971-
AssertionDsc dsc = CreateEmptyAssertion(comp);
8972-
dsc.m_assertionKind = FromVNFunc(relop);
8973-
dsc.m_op1.m_kind = O1K_VN;
8974-
dsc.m_op1.m_vn = op1VN;
8975-
dsc.m_op2.m_kind = O2K_VN;
8976-
dsc.m_op2.m_vn = op2VN;
8955+
AssertionDsc dsc = CreateEmptyAssertion(comp);
8956+
dsc.m_assertionKind = FromVNFunc(relop);
8957+
dsc.m_op1.m_kind = O1K_VN;
8958+
dsc.m_op1.m_vn = op1VN;
8959+
dsc.m_op2.m_kind = O2K_VN_ADD_CNS;
8960+
dsc.m_op2.m_vn = op2VN;
8961+
dsc.m_op2.m_icon.m_iconVal = 0; // TODO-CQ: consider decomposing VN to VN* + CNS if beneficial.
8962+
dsc.m_op2.m_isVNNeverNegative = false;
89778963
return dsc;
89788964
}
89798965
};

src/coreclr/jit/rangecheck.cpp

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,11 +1126,10 @@ void RangeCheck::MergeEdgeAssertionsWorker(Compiler* comp
11261126
// We can deduce normalLclVN's upper bound to be preferredBoundVN - CNS1
11271127
// Example: "(uint)(i + 2) < (uint)span.Length"
11281128
if (canUseCheckedBounds && curAssertion.KindIs(Compiler::OAK_LT_UN) &&
1129-
curAssertion.GetOp2().KindIs(Compiler::O2K_CHECKED_BOUND_ADD_CNS) &&
1129+
curAssertion.GetOp2().KindIs(Compiler::O2K_VN_ADD_CNS) &&
11301130
// Normally, checked bound doesn't directly imply non-negativity, so we check
11311131
// that explicitly here:
1132-
curAssertion.GetOp2().IsCheckedBoundNeverNegative() &&
1133-
(curAssertion.GetOp2().GetCheckedBound() == preferredBoundVN) &&
1132+
curAssertion.GetOp2().IsVNNeverNegative() && (curAssertion.GetOp2().GetVN() == preferredBoundVN) &&
11341133
(curAssertion.GetOp1().GetVN() != normalLclVN))
11351134
{
11361135
ValueNum addOpVN;
@@ -1164,21 +1163,20 @@ void RangeCheck::MergeEdgeAssertionsWorker(Compiler* comp
11641163
}
11651164
}
11661165
// If we're not allowed to emit keBinOpArray, we still can deduce something useful from
1167-
// O2K_CHECKED_BOUND_ADD_CNS for never negative checked bounds
1166+
// O2K_VN_ADD_CNS for never negative checked bounds
11681167
//
11691168
// Example: "(uint)normalLclVN < span.Length" means normalLclVN's range is [0..INT32_MAX-1]
11701169
// Example: "(uint)normalLclVN <= array.Length" means normalLclVN's range is [0..Array.MaxLength]
11711170
//
11721171
else if (!canUseCheckedBounds && curAssertion.IsRelop() && (curAssertion.GetOp1().GetVN() == normalLclVN) &&
1173-
(curAssertion.GetOp2().KindIs(Compiler::O2K_CHECKED_BOUND_ADD_CNS)) &&
1174-
(curAssertion.GetOp2().IsCheckedBoundNeverNegative()) &&
1175-
(curAssertion.GetOp2().GetCheckedBoundConstant() == 0))
1172+
(curAssertion.GetOp2().KindIs(Compiler::O2K_VN_ADD_CNS)) &&
1173+
(curAssertion.GetOp2().IsVNNeverNegative()) && (curAssertion.GetOp2().GetCns() == 0))
11761174
{
1177-
// We can assume that the checked bound is within [0..INT32_MAX] thanks to IsCheckedBoundNeverNegative,
1175+
// We can assume that the checked bound is within [0..INT32_MAX] thanks to IsVNNeverNegative,
11781176
// but let's see if we can do better: we could call GetRangeFromAssertions on the checked bound's VN,
11791177
// but that may lead to infinite recursion. Also, the checked bound is usually either arr.Length or
11801178
// span.Length anyway.
1181-
ValueNum checkedBoundVN = curAssertion.GetOp2().GetCheckedBound();
1179+
ValueNum checkedBoundVN = curAssertion.GetOp2().GetVN();
11821180

11831181
int maxValue = INT32_MAX;
11841182
if (comp->vnStore->IsVNArrLen(checkedBoundVN))
@@ -1197,12 +1195,18 @@ void RangeCheck::MergeEdgeAssertionsWorker(Compiler* comp
11971195
cmpOper = Compiler::AssertionDsc::ToCompareOper(curAssertion.GetKind(), &isUnsigned);
11981196
limit = Limit(Limit::keConstant, maxValue);
11991197
}
1200-
// Current assertion is of the form "i <relop> (checkedBndVN + cns)"
1198+
// Current assertion is of the form "i <relop> (vn + cns)" where vn is a real
1199+
// (length-like) checked bound. The arbitrary-VN sub-form of O2K_VN_ADD_CNS (created by
1200+
// CreateRelopVN, where op2.vn is not a checked bound) is intentionally excluded here so
1201+
// that it never flows into a keBinOpArray Limit, whose Range::IsValid / Range::Widen
1202+
// rules implicitly assume the VN refers to a length-like (checked-bound-shaped) quantity.
1203+
// Such cases fall through to the general "X <relop> Y" branch below.
12011204
else if (curAssertion.KindIs(Compiler::OAK_GE, Compiler::OAK_GT, Compiler::OAK_LE, Compiler::OAK_LT) &&
1202-
curAssertion.GetOp2().KindIs(Compiler::O2K_CHECKED_BOUND_ADD_CNS))
1205+
curAssertion.GetOp2().KindIs(Compiler::O2K_VN_ADD_CNS) &&
1206+
comp->vnStore->IsVNCheckedBound(curAssertion.GetOp2().GetVN()))
12031207
{
1204-
const ValueNum boundVN = curAssertion.GetOp2().GetCheckedBound();
1205-
const int boundCns = curAssertion.GetOp2().GetCheckedBoundConstant();
1208+
const ValueNum boundVN = curAssertion.GetOp2().GetVN();
1209+
const int boundCns = curAssertion.GetOp2().GetCns();
12061210

12071211
// Check whether normalLclVN VN-equals the op2 expression "(boundVN + boundCns)":
12081212
// - trivially, when boundCns is 0 and normalLclVN == boundVN, or
@@ -1234,7 +1238,7 @@ void RangeCheck::MergeEdgeAssertionsWorker(Compiler* comp
12341238
// Check if it's "Const <relop> (checkedBndVN + cns)" or in other words "Const <relop> normalLclVN"
12351239
// If normalLclVN VN-equals (checkedBndVN + cns), we can deduce "normalLclVN <relop> Const"
12361240
//
1237-
// Example: We created "O1K_VN(vn1) - OAK_GT - O2K_CHECKED_BOUND_ADD_CNS(vn2, 0)"
1241+
// Example: We created "O1K_VN(vn1) - OAK_GT - O2K_VN_ADD_CNS(vn2, 0)"
12381242
// if vn1 is a constant (say, 100) and vn2 is our normalLclVN, we can deduce "normalLclVN < 100"
12391243
//
12401244
if (comp->vnStore->IsVNInt32Constant(curAssertion.GetOp1().GetVN()))
@@ -1321,10 +1325,10 @@ void RangeCheck::MergeEdgeAssertionsWorker(Compiler* comp
13211325
{
13221326
// IsBoundsCheckNoThrow is "op1VN (Idx) LT_UN op2VN (Len)"
13231327
ValueNum indexVN = curAssertion.GetOp1().GetVN();
1324-
ValueNum lenVN = curAssertion.GetOp2().GetCheckedBound();
1328+
ValueNum lenVN = curAssertion.GetOp2().GetVN();
13251329

1326-
assert(curAssertion.GetOp2().GetCheckedBoundConstant() == 0);
1327-
assert(curAssertion.GetOp2().IsCheckedBoundNeverNegative());
1330+
assert(curAssertion.GetOp2().GetCns() == 0);
1331+
assert(curAssertion.GetOp2().IsVNNeverNegative());
13281332

13291333
if (normalLclVN == indexVN)
13301334
{
@@ -1404,7 +1408,8 @@ void RangeCheck::MergeEdgeAssertionsWorker(Compiler* comp
14041408
// if (normalLclVN > a) // a is an unknown VN with [11..99] range derived from assertions.
14051409
// {
14061410
//
1407-
else if (curAssertion.IsRelop() && curAssertion.GetOp2().KindIs(Compiler::O2K_VN) &&
1411+
else if (curAssertion.IsRelop() && curAssertion.GetOp2().KindIs(Compiler::O2K_VN_ADD_CNS) &&
1412+
(curAssertion.GetOp2().GetCns() == 0) &&
14081413
(curAssertion.GetOp1().GetVN() == normalLclVN || curAssertion.GetOp2().GetVN() == normalLclVN))
14091414
{
14101415
ValueNum op1VN = curAssertion.GetOp1().GetVN();

0 commit comments

Comments
 (0)