Skip to content

Commit 3f82e12

Browse files
committed
Implement in redundant-branch phase
1 parent cc4db9b commit 3f82e12

File tree

1 file changed

+168
-0
lines changed

1 file changed

+168
-0
lines changed

src/coreclr/jit/redundantbranchopts.cpp

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,142 @@ struct RelopImplicationRule
152152
bool reverse;
153153
};
154154

155+
enum ImpliedRangeCheckStatus
156+
{
157+
Unknown,
158+
NeverIntersects,
159+
AlwaysIncluded
160+
};
161+
162+
//------------------------------------------------------------------------
163+
// IsRange2ImpliedByRange1: Given two range checks, determine if the 2nd one is redundant or not.
164+
// It is assumed, that the both range checks are for the same X on LHS of the comparison operators.
165+
// e.g. "x > 100 && x > 10" -> AlwaysIncluded
166+
//
167+
// Arguments:
168+
// oper1 - the first range check operator [X *oper1* bound1 ]
169+
// bound1 - the first range check constant bound [X oper1 *bound1*]
170+
// oper2 - the second range check operator [X *oper2* bound2 ]
171+
// bound2 - the second range check constant bound [X oper2 *bound2*]
172+
//
173+
// Returns:
174+
// "AlwaysIncluded" means that the 2nd range check is always "true"
175+
// "NeverIntersects" means that the 2nd range check is never "true"
176+
// "Unknown" means that we can't determine if the 2nd range check is redundant or not.
177+
//
178+
ImpliedRangeCheckStatus IsRange2ImpliedByRange1(genTreeOps oper1, ssize_t bound1, genTreeOps oper2, ssize_t bound2)
179+
{
180+
struct IntegralRange
181+
{
182+
ssize_t startIncl;
183+
ssize_t endIncl;
184+
};
185+
186+
IntegralRange range1 = {INTPTR_MIN, INTPTR_MAX};
187+
IntegralRange range2 = {INTPTR_MIN, INTPTR_MAX};
188+
189+
// Update ranges based on inputs
190+
auto setRange = [](genTreeOps oper, ssize_t bound, IntegralRange* range) -> bool {
191+
switch (oper)
192+
{
193+
case GT_LT:
194+
// x < cns -> [INTPTR_MIN, cns - 1]
195+
if (bound == INTPTR_MIN)
196+
{
197+
// overflows
198+
return false;
199+
}
200+
range->endIncl = bound - 1;
201+
return true;
202+
203+
case GT_LE:
204+
// x <= cns -> [INTPTR_MIN, cns]
205+
range->endIncl = bound;
206+
return true;
207+
208+
case GT_GT:
209+
// x > cns -> [cns + 1, INTPTR_MAX]
210+
if (bound == INTPTR_MAX)
211+
{
212+
// overflows
213+
return false;
214+
}
215+
range->startIncl = bound + 1;
216+
return true;
217+
218+
case GT_GE:
219+
// x >= cns -> [cns, INTPTR_MAX]
220+
range->startIncl = bound;
221+
return true;
222+
223+
case GT_EQ:
224+
// x == cns -> [cns, cns]
225+
range->startIncl = bound;
226+
range->endIncl = bound;
227+
return true;
228+
229+
case GT_NE:
230+
// special cased below (we can't represent it as a range)
231+
return true;
232+
233+
default:
234+
// unsupported operator
235+
return false;
236+
}
237+
};
238+
239+
const bool success1 = setRange(oper1, bound1, &range1);
240+
const bool success2 = setRange(oper2, bound2, &range2);
241+
if (!success1 || !success2)
242+
{
243+
return Unknown;
244+
}
245+
246+
// NE is special since we can't represent it as a range, let's only handle it if it's the 2nd operand
247+
// for simplicity (driven by jit-diffs).
248+
if (oper1 == GT_NE)
249+
{
250+
return Unknown;
251+
}
252+
if (oper2 == GT_NE)
253+
{
254+
if ((bound2 < range1.startIncl) || (bound2 > range1.endIncl))
255+
{
256+
// "x > 100 && x != 10", the 2nd range check is always true
257+
return AlwaysIncluded;
258+
}
259+
if ((range1.startIncl == bound2) && (range1.endIncl == bound2))
260+
{
261+
// "x == 100 && x != 100", the 2nd range check is never true
262+
return NeverIntersects;
263+
}
264+
return Unknown;
265+
}
266+
267+
// If ranges never intersect, then the 2nd range is never "true"
268+
if ((range1.startIncl > range2.endIncl) || (range2.startIncl > range1.endIncl))
269+
{
270+
// E.g.:
271+
//
272+
// range1: [100 .. INT_MAX]
273+
// range2: [INT_MIN .. 10]
274+
return NeverIntersects;
275+
}
276+
277+
// Check if range1 is fully included into range2
278+
if ((range2.startIncl <= range1.startIncl) && (range1.endIncl <= range2.endIncl))
279+
{
280+
// E.g.:
281+
//
282+
// range1: [100 .. INT_MAX]
283+
// range2: [10 .. INT_MAX]
284+
return AlwaysIncluded;
285+
}
286+
287+
// Ranges intersect, but we can't determine if the 2nd range is redundant or not.
288+
return Unknown;
289+
}
290+
155291
//------------------------------------------------------------------------
156292
// s_implicationRules: rule table for unrelated relops
157293
//
@@ -338,6 +474,38 @@ void Compiler::optRelopImpliesRelop(RelopImplicationInfo* rii)
338474
}
339475
}
340476
}
477+
// Given R(x, cns1) and R*(x, cns2) see if we can infer R* from R.
478+
else if ((treeApp.m_args[0] == domApp.m_args[0]) && vnStore->IsVNConstant(treeApp.m_args[1]) &&
479+
vnStore->IsVNConstant(domApp.m_args[1]) && varTypeIsIntOrI(vnStore->TypeOfVN(treeApp.m_args[1])) &&
480+
varTypeIsIntOrI(vnStore->TypeOfVN(domApp.m_args[1])))
481+
{
482+
const ssize_t domCns = vnStore->CoercedConstantValue<ssize_t>(domApp.m_args[1]);
483+
const ssize_t treeCns = vnStore->CoercedConstantValue<ssize_t>(treeApp.m_args[1]);
484+
485+
// We currently don't handle VNF_relop_UN funcs here, they'll be ignored.
486+
const genTreeOps treeOper = static_cast<genTreeOps>(treeApp.m_func);
487+
const genTreeOps domOper = static_cast<genTreeOps>(domApp.m_func);
488+
489+
bool canInferFromTrue = true;
490+
ImpliedRangeCheckStatus result = IsRange2ImpliedByRange1(domOper, domCns, treeOper, treeCns);
491+
if ((result == Unknown) && GenTree::OperIsCompare(domOper))
492+
{
493+
// Reverse the dominating compare and try again, if it succeeds, we can infer from "false".
494+
result = IsRange2ImpliedByRange1(GenTree::ReverseRelop(domOper), domCns, treeOper, treeCns);
495+
canInferFromTrue = false;
496+
}
497+
498+
// TODO: handle NeverIntersects case.
499+
if (result == AlwaysIncluded)
500+
{
501+
rii->canInfer = true;
502+
rii->vnRelation = ValueNumStore::VN_RELATION_KIND::VRK_Inferred;
503+
rii->canInferFromTrue = canInferFromTrue;
504+
rii->canInferFromFalse = !canInferFromTrue;
505+
rii->reverseSense = false;
506+
return;
507+
}
508+
}
341509
}
342510

343511
// See if dominating compare is a compound comparison that might

0 commit comments

Comments
 (0)