Skip to content

Commit 68fe6b8

Browse files
authored
JIT: one-sided inference from unrelated predicates (#72979)
Simplistic version where we just look for identical operands.
1 parent 06350b7 commit 68fe6b8

File tree

4 files changed

+174
-15
lines changed

4 files changed

+174
-15
lines changed

src/coreclr/jit/jitconfigvalues.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ CONFIG_INTEGER(JitDoLoopHoisting, W("JitDoLoopHoisting"), 1) // Perform loop h
407407
CONFIG_INTEGER(JitDoLoopInversion, W("JitDoLoopInversion"), 1) // Perform loop inversion on "for/while" loops
408408
CONFIG_INTEGER(JitDoRangeAnalysis, W("JitDoRangeAnalysis"), 1) // Perform range check analysis
409409
CONFIG_INTEGER(JitDoRedundantBranchOpts, W("JitDoRedundantBranchOpts"), 1) // Perform redundant branch optimizations
410+
CONFIG_STRING(JitEnableRboRange, W("JitEnableRboRange"))
410411

411412
CONFIG_INTEGER(JitDoSsa, W("JitDoSsa"), 1) // Perform Static Single Assignment (SSA) numbering on the variables
412413
CONFIG_INTEGER(JitDoValueNumber, W("JitDoValueNumber"), 1) // Perform value numbering on method expressions

src/coreclr/jit/redundantbranchopts.cpp

Lines changed: 160 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,76 @@ struct RelopImplicationInfo
115115
ValueNumStore::VN_RELATION_KIND vnRelation = ValueNumStore::VN_RELATION_KIND::VRK_Same;
116116
// Can we draw an inference?
117117
bool canInfer = false;
118-
// If canInfer and ominating relop is true, can we infer value of dominated relop?
118+
// If canInfer and dominating relop is true, can we infer value of dominated relop?
119119
bool canInferFromTrue = true;
120120
// If canInfer and dominating relop is false, can we infer value of dominated relop?
121121
bool canInferFromFalse = true;
122122
// Reverse the sense of the inference
123123
bool reverseSense = false;
124124
};
125125

126+
//------------------------------------------------------------------------
127+
// RelopImplicationRule
128+
//
129+
// A rule allowing inference between two otherwise unrelated relops.
130+
// Related relops are handled via s_vnRelations above.
131+
//
132+
struct RelopImplicationRule
133+
{
134+
VNFunc domRelop;
135+
bool canInferFromTrue;
136+
bool canInferFromFalse;
137+
VNFunc treeRelop;
138+
bool reverse;
139+
};
140+
141+
//------------------------------------------------------------------------
142+
// s_implicationRules: rule table for unrelated relops
143+
//
144+
// clang-format off
145+
//
146+
#define V(x) (VNFunc)GT_##x
147+
148+
static const RelopImplicationRule s_implicationRules[] =
149+
{
150+
// EQ
151+
{V(EQ), true, false, V(GE), false},
152+
{V(EQ), true, false, V(LE), false},
153+
{V(EQ), true, false, V(GT), true},
154+
{V(EQ), true, false, V(LT), true},
155+
156+
// NE
157+
{V(NE), false, true, V(GE), true},
158+
{V(NE), false, true, V(LE), true},
159+
{V(NE), false, true, V(GT), false},
160+
{V(NE), false, true, V(LT), false},
161+
162+
// LE
163+
{V(LE), false, true, V(EQ), false},
164+
{V(LE), false, true, V(NE), true},
165+
{V(LE), false, true, V(GE), true},
166+
{V(LE), false, true, V(LT), false},
167+
168+
// GT
169+
{V(GT), true, false, V(EQ), true},
170+
{V(GT), true, false, V(NE), false},
171+
{V(GT), true, false, V(GE), false},
172+
{V(GT), true, false, V(LT), true},
173+
174+
// GE
175+
{V(GE), false, true, V(EQ), false},
176+
{V(GE), false, true, V(NE), true},
177+
{V(GE), false, true, V(LE), true},
178+
{V(GE), false, true, V(GT), false},
179+
180+
// LT
181+
{V(LT), true, false, V(EQ), true},
182+
{V(LT), true, false, V(NE), false},
183+
{V(LT), true, false, V(LE), false},
184+
{V(LT), true, false, V(GT), true},
185+
};
186+
// clang-format on
187+
126188
//------------------------------------------------------------------------
127189
// optRedundantBranch: try and optimize a possibly redundant branch
128190
//
@@ -146,9 +208,14 @@ struct RelopImplicationInfo
146208
// a false taken branch, so we need to invert the sense of our
147209
// inferences.
148210
//
149-
// Note we could also infer the tree relop's value from other
211+
// We can also partially infer the tree relop's value from other
150212
// dominating relops, for example, (x >= 0) dominating (x > 0).
151-
// That is left as a future enhancement.
213+
//
214+
// We don't get all the cases here we could. Still to do:
215+
// * two unsigned compares, same operands
216+
// * mixture of signed/unsigned compares, same operands
217+
// * mixture of compares, one operand same, other operands different constants
218+
// x > 1 ==> x >= 0
152219
//
153220
void Compiler::optRelopImpliesRelop(RelopImplicationInfo* rii)
154221
{
@@ -178,15 +245,72 @@ void Compiler::optRelopImpliesRelop(RelopImplicationInfo* rii)
178245

179246
genTreeOps const oper = genTreeOps(funcApp.m_func);
180247

248+
// Exclude floating point relops.
249+
//
250+
if (varTypeIsFloating(vnStore->TypeOfVN(funcApp.m_args[0])))
251+
{
252+
return;
253+
}
254+
255+
#ifdef DEBUG
256+
static ConfigMethodRange JitEnableRboRange;
257+
JitEnableRboRange.EnsureInit(JitConfig.JitEnableRboRange());
258+
const unsigned hash = impInlineRoot()->info.compMethodHash();
259+
const bool inRange = JitEnableRboRange.Contains(hash);
260+
#else
261+
const bool inRange = true;
262+
#endif
263+
264+
// Dominating compare has the form R(x,y)
265+
// See if tree compare has the form R*(x,y) or R*(y,x) where we can infer R* from R
266+
//
267+
// Could also extend to the unsigned VN relops.
268+
//
269+
VNFuncApp treeApp;
270+
if (inRange && GenTree::OperIsCompare(oper) && vnStore->GetVNFunc(rii->treeNormVN, &treeApp))
271+
{
272+
genTreeOps const treeOper = genTreeOps(treeApp.m_func);
273+
genTreeOps domOper = oper;
274+
275+
if (((treeApp.m_args[0] == funcApp.m_args[0]) && (treeApp.m_args[1] == funcApp.m_args[1])) ||
276+
((treeApp.m_args[0] == funcApp.m_args[1]) && (treeApp.m_args[1] == funcApp.m_args[0])))
277+
{
278+
const bool swapped = (treeApp.m_args[0] == funcApp.m_args[1]);
279+
280+
if (swapped)
281+
{
282+
domOper = GenTree::SwapRelop(domOper);
283+
}
284+
285+
for (const RelopImplicationRule& rule : s_implicationRules)
286+
{
287+
if ((rule.domRelop == (VNFunc)domOper) && (rule.treeRelop == (VNFunc)treeOper))
288+
{
289+
rii->canInfer = true;
290+
rii->vnRelation = ValueNumStore::VN_RELATION_KIND::VRK_Inferred;
291+
rii->canInferFromTrue = rule.canInferFromTrue;
292+
rii->canInferFromFalse = rule.canInferFromFalse;
293+
rii->reverseSense = rule.reverse;
294+
295+
JITDUMP("Can infer %s from [%s] %s\n", GenTree::OpName(treeOper),
296+
rii->canInferFromTrue ? "true" : "false", GenTree::OpName(oper));
297+
return;
298+
}
299+
}
300+
}
301+
}
302+
303+
// See if dominating compare is a compound comparison that might
304+
// tell us the value of the tree compare.
305+
//
181306
// Look for {EQ,NE}({AND,OR,NOT}, 0)
182307
//
183308
if (!GenTree::StaticOperIs(oper, GT_EQ, GT_NE))
184309
{
185310
return;
186311
}
187312

188-
const ValueNum constantVN = funcApp.m_args[1];
189-
if (constantVN != vnStore->VNZeroForType(TYP_INT))
313+
if (funcApp.m_args[1] != vnStore->VNZeroForType(TYP_INT))
190314
{
191315
return;
192316
}
@@ -309,11 +433,14 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
309433
//
310434
if (vnStore->IsVNConstant(treeNormVN))
311435
{
312-
313436
relopValue = (treeNormVN == vnStore->VNZeroForType(TYP_INT)) ? 0 : 1;
314437
JITDUMP("Relop [%06u] " FMT_BB " has known value %s\n ", dspTreeID(tree), block->bbNum,
315438
relopValue == 0 ? "false" : "true");
316439
}
440+
else
441+
{
442+
JITDUMP("Relop [%06u] " FMT_BB " value unknown, trying inference\n", dspTreeID(tree), block->bbNum);
443+
}
317444

318445
bool trySpeculativeDom = false;
319446
while ((relopValue == -1) && !trySpeculativeDom)
@@ -370,11 +497,26 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
370497
return false;
371498
}
372499

500+
// Was this an inference from an unrelated relop (GE => GT, say)?
501+
//
502+
const bool domIsInferredRelop = (rii.vnRelation == ValueNumStore::VN_RELATION_KIND::VRK_Inferred);
503+
373504
// The compare in "tree" is redundant.
374505
// Is there a unique path from the dominating compare?
375506
//
376-
JITDUMP("\nDominator " FMT_BB " of " FMT_BB " has relop with %s liberal VN\n", domBlock->bbNum,
377-
block->bbNum, ValueNumStore::VNRelationString(rii.vnRelation));
507+
if (domIsInferredRelop)
508+
{
509+
// This inference should be one-sided
510+
//
511+
assert(rii.canInferFromTrue ^ rii.canInferFromFalse);
512+
JITDUMP("\nDominator " FMT_BB " of " FMT_BB " has same VN operands but different relop\n",
513+
domBlock->bbNum, block->bbNum);
514+
}
515+
else
516+
{
517+
JITDUMP("\nDominator " FMT_BB " of " FMT_BB " has relop with %s liberal VN\n", domBlock->bbNum,
518+
block->bbNum, ValueNumStore::VNRelationString(rii.vnRelation));
519+
}
378520
DISPTREE(domCmpTree);
379521
JITDUMP(" Redundant compare; current relop:\n");
380522
DISPTREE(tree);
@@ -415,7 +557,7 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
415557
{
416558
// Taken jump in dominator reaches, fall through doesn't; relop must be true/false.
417559
//
418-
const bool relopIsTrue = rii.reverseSense ^ domIsSameRelop;
560+
const bool relopIsTrue = rii.reverseSense ^ (domIsSameRelop | domIsInferredRelop);
419561
JITDUMP("Jump successor " FMT_BB " of " FMT_BB " reaches, relop [%06u] must be %s\n",
420562
domBlock->bbJumpDest->bbNum, domBlock->bbNum, dspTreeID(tree),
421563
relopIsTrue ? "true" : "false");
@@ -426,14 +568,14 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
426568
{
427569
// Fall through from dominator reaches, taken jump doesn't; relop must be false/true.
428570
//
429-
const bool relopIsFalse = rii.reverseSense ^ domIsSameRelop;
571+
const bool relopIsFalse = rii.reverseSense ^ (domIsSameRelop | domIsInferredRelop);
430572
JITDUMP("Fall through successor " FMT_BB " of " FMT_BB " reaches, relop [%06u] must be %s\n",
431573
domBlock->bbNext->bbNum, domBlock->bbNum, dspTreeID(tree),
432574
relopIsFalse ? "false" : "true");
433575
relopValue = relopIsFalse ? 0 : 1;
434576
break;
435577
}
436-
else
578+
else if (!falseReaches && !trueReaches)
437579
{
438580
// No apparent path from the dominating BB.
439581
//
@@ -446,8 +588,15 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
446588
//
447589
// No point in looking further up the tree.
448590
//
591+
JITDUMP("inference failed -- no apparent path, will stop looking\n");
449592
break;
450593
}
594+
else
595+
{
596+
// Keep looking up the dom tree
597+
//
598+
JITDUMP("inference failed -- will keep looking higher\n");
599+
}
451600
}
452601
}
453602
}

src/coreclr/jit/valuenum.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5250,6 +5250,7 @@ bool ValueNumStore::IsVNHandle(ValueNum vn)
52505250
//
52515251
// Note:
52525252
// If "vn" corresponds to (x > y), the resulting VN corresponds to
5253+
// VRK_Inferred (x ? y) (NoVN)
52535254
// VRK_Same (x > y)
52545255
// VRK_Swap (y < x)
52555256
// VRK_Reverse (x <= y)
@@ -5267,6 +5268,11 @@ ValueNum ValueNumStore::GetRelatedRelop(ValueNum vn, VN_RELATION_KIND vrk)
52675268
return vn;
52685269
}
52695270

5271+
if (vrk == VN_RELATION_KIND::VRK_Inferred)
5272+
{
5273+
return NoVN;
5274+
}
5275+
52705276
if (vn == NoVN)
52715277
{
52725278
return NoVN;
@@ -5385,6 +5391,8 @@ const char* ValueNumStore::VNRelationString(VN_RELATION_KIND vrk)
53855391
{
53865392
switch (vrk)
53875393
{
5394+
case VN_RELATION_KIND::VRK_Inferred:
5395+
return "inferred";
53885396
case VN_RELATION_KIND::VRK_Same:
53895397
return "same";
53905398
case VN_RELATION_KIND::VRK_Reverse:

src/coreclr/jit/valuenum.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -944,12 +944,9 @@ class ValueNumStore
944944
// Returns true iff the VN represents a relop
945945
bool IsVNRelop(ValueNum vn);
946946

947-
// Given VN(x > y), return VN(y > x), VN(x <= y) or VN(y >= x)
948-
//
949-
// If vn is not a relop, return NoVN.
950-
//
951947
enum class VN_RELATION_KIND
952948
{
949+
VRK_Inferred, // (x ? y)
953950
VRK_Same, // (x > y)
954951
VRK_Swap, // (y > x)
955952
VRK_Reverse, // (x <= y)
@@ -960,6 +957,10 @@ class ValueNumStore
960957
static const char* VNRelationString(VN_RELATION_KIND vrk);
961958
#endif
962959

960+
// Given VN(x > y), return VN(y > x), VN(x <= y) or VN(y >= x)
961+
//
962+
// If vn is not a relop, return NoVN.
963+
//
963964
ValueNum GetRelatedRelop(ValueNum vn, VN_RELATION_KIND vrk);
964965

965966
// Convert a vartype_t to the value number's storage type for that vartype_t.

0 commit comments

Comments
 (0)