Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT: Optimize range checks for a[i & C], a[i % c] and a[(i & c1)>>c2)] patterns #1644

Merged
merged 11 commits into from
Mar 4, 2020
46 changes: 43 additions & 3 deletions src/coreclr/src/jit/rangecheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -773,11 +773,46 @@ void RangeCheck::MergeAssertion(BasicBlock* block, GenTree* op, Range* pRange DE
// Compute the range for a binary operation.
Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool monIncreasing DEBUGARG(int indent))
{
assert(binop->OperIs(GT_ADD));
assert(binop->OperIs(GT_ADD, GT_AND, GT_RSH, GT_UMOD));

GenTree* op1 = binop->gtGetOp1();
GenTree* op2 = binop->gtGetOp2();

if (binop->OperIs(GT_AND, GT_RSH, GT_UMOD))
{
if (!op2->IsIntCnsFitsInI32())
{
// only cns is supported for op2 at the moment for &,%,>> operators
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
return Range(Limit::keUnknown);
}

int icon = -1;
if (binop->OperIs(GT_AND))
{
// x & cns -> [0..cns]
icon = static_cast<int>(op2->AsIntCon()->IconValue());
}
else if (binop->OperIs(GT_UMOD))
{
// x % cns -> [0..cns-1]
icon = static_cast<int>(op2->AsIntCon()->IconValue()) - 1;
}
else if (binop->OperIs(GT_RSH) && op1->OperIs(GT_AND) && op1->AsOp()->gtGetOp2()->IsIntCnsFitsInI32())
{
// (x & cns1) >> cns2 -> [0..cns1>>cns2]
icon = static_cast<int>(op1->AsOp()->gtGetOp2()->AsIntCon()->IconValue()) >>
static_cast<int>(op2->AsIntCon()->IconValue());
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
}

if (icon < 0)
{
return Range(Limit::keUnknown);
}
Range range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, icon));
JITDUMP("Limit range to %s\n", range.ToString(m_pCompiler->getAllocatorDebugOnly()));
return range;
}

EgorBo marked this conversation as resolved.
Show resolved Hide resolved
Range* op1RangeCached = nullptr;
Range op1Range = Limit(Limit::keUndef);
// Check if the range value is already cached.
Expand Down Expand Up @@ -1032,6 +1067,11 @@ bool RangeCheck::ComputeDoesOverflow(BasicBlock* block, GenTree* expr)
{
overflows = DoesBinOpOverflow(block, expr->AsOp());
}
// GT_AND, GT_UMOD and GT_RSH don't overflow
else if (expr->OperIs(GT_AND, GT_RSH, GT_UMOD))
{
overflows = false;
}
// Walk through phi arguments to check if phi arguments involve arithmetic that overflows.
else if (expr->OperGet() == GT_PHI)
{
Expand Down Expand Up @@ -1117,8 +1157,8 @@ Range RangeCheck::ComputeRange(BasicBlock* block, GenTree* expr, bool monIncreas
range = ComputeRangeForLocalDef(block, expr->AsLclVarCommon(), monIncreasing DEBUGARG(indent + 1));
MergeAssertion(block, expr, &range DEBUGARG(indent + 1));
}
// If add, then compute the range for the operands and add them.
else if (expr->OperGet() == GT_ADD)
// compute the range for binary operation
else if (expr->OperIs(GT_ADD, GT_AND, GT_RSH, GT_UMOD))
{
range = ComputeRangeForBinOp(block, expr->AsOp(), monIncreasing DEBUGARG(indent + 1));
}
Expand Down