Skip to content

Commit 62528e7

Browse files
EgorBojakobbotsch
andauthored
JIT: Fold more nullchecks (#111985)
Co-authored-by: Jakob Botsch Nielsen <[email protected]>
1 parent 0530b55 commit 62528e7

File tree

3 files changed

+109
-37
lines changed

3 files changed

+109
-37
lines changed

src/coreclr/jit/assertionprop.cpp

Lines changed: 94 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4367,6 +4367,47 @@ GenTree* Compiler::optAssertionProp_RelOp(ASSERT_VALARG_TP assertions, GenTree*
43674367
return optAssertionPropLocal_RelOp(assertions, tree, stmt);
43684368
}
43694369

4370+
//--------------------------------------------------------------------------------
4371+
// optVisitReachingAssertions: given a vn, call the specified callback function on all
4372+
// the assertions that reach it via PHI definitions if any.
4373+
//
4374+
// Arguments:
4375+
// vn - The vn to visit all the reaching assertions for
4376+
// argVisitor - The callback function to call on the vn and its reaching assertions
4377+
//
4378+
// Return Value:
4379+
// AssertVisit::Aborted - an argVisitor returned AssertVisit::Abort, we stop the walk and return
4380+
// AssertVisit::Continue - all argVisitor returned AssertVisit::Continue
4381+
//
4382+
template <typename TAssertVisitor>
4383+
Compiler::AssertVisit Compiler::optVisitReachingAssertions(ValueNum vn, TAssertVisitor argVisitor)
4384+
{
4385+
VNPhiDef phiDef;
4386+
if (!vnStore->GetPhiDef(vn, &phiDef))
4387+
{
4388+
// We assume that the caller already checked assertions for the current block, so we're
4389+
// interested only in assertions for PHI definitions.
4390+
return AssertVisit::Abort;
4391+
}
4392+
4393+
LclSsaVarDsc* ssaDef = lvaGetDesc(phiDef.LclNum)->GetPerSsaData(phiDef.SsaDef);
4394+
GenTreeLclVarCommon* node = ssaDef->GetDefNode();
4395+
assert(node->IsPhiDefn());
4396+
4397+
for (GenTreePhi::Use& use : node->Data()->AsPhi()->Uses())
4398+
{
4399+
GenTreePhiArg* phiArg = use.GetNode()->AsPhiArg();
4400+
const ValueNum phiArgVN = vnStore->VNConservativeNormalValue(phiArg->gtVNPair);
4401+
ASSERT_TP assertions = optGetEdgeAssertions(ssaDef->GetBlock(), phiArg->gtPredBB);
4402+
if (argVisitor(phiArgVN, assertions) == AssertVisit::Abort)
4403+
{
4404+
// The visitor wants to abort the walk.
4405+
return AssertVisit::Abort;
4406+
}
4407+
}
4408+
return AssertVisit::Continue;
4409+
}
4410+
43704411
//------------------------------------------------------------------------
43714412
// optAssertionProp: try and optimize a relop via assertion propagation
43724413
//
@@ -4380,6 +4421,8 @@ GenTree* Compiler::optAssertionProp_RelOp(ASSERT_VALARG_TP assertions, GenTree*
43804421
//
43814422
GenTree* Compiler::optAssertionPropGlobal_RelOp(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt)
43824423
{
4424+
assert(!optLocalAssertionProp);
4425+
43834426
GenTree* newTree = tree;
43844427
GenTree* op1 = tree->AsOp()->gtOp1;
43854428
GenTree* op2 = tree->AsOp()->gtOp2;
@@ -4463,6 +4506,24 @@ GenTree* Compiler::optAssertionPropGlobal_RelOp(ASSERT_VALARG_TP assertions, Gen
44634506
return nullptr;
44644507
}
44654508

4509+
// See if we have "PHI ==/!= null" tree. If so, we iterate over all PHI's arguments,
4510+
// and if all of them are known to be non-null, we can bash the comparison to true/false.
4511+
if (op2->IsIntegralConst(0) && op1->TypeIs(TYP_REF))
4512+
{
4513+
auto visitor = [this](ValueNum reachingVN, ASSERT_TP reachingAssertions) {
4514+
return optAssertionVNIsNonNull(reachingVN, reachingAssertions) ? AssertVisit::Continue : AssertVisit::Abort;
4515+
};
4516+
4517+
ValueNum op1vn = vnStore->VNConservativeNormalValue(op1->gtVNPair);
4518+
if (optVisitReachingAssertions(op1vn, visitor) == AssertVisit::Continue)
4519+
{
4520+
JITDUMP("... all of PHI's arguments are never null!\n");
4521+
assert(newTree->OperIs(GT_EQ, GT_NE));
4522+
newTree = tree->OperIs(GT_EQ) ? gtNewIconNode(0) : gtNewIconNode(1);
4523+
return optAssertionProp_Update(newTree, tree, stmt);
4524+
}
4525+
}
4526+
44664527
// Find an equal or not equal assertion involving "op1" and "op2".
44674528
index = optGlobalAssertionIsEqualOrNotEqual(assertions, op1, op2);
44684529

@@ -5083,31 +5144,19 @@ bool Compiler::optAssertionVNIsNonNull(ValueNum vn, ASSERT_VALARG_TP assertions)
50835144
return true;
50845145
}
50855146

5086-
// Check each assertion to find if we have a vn != null assertion.
5087-
//
5088-
BitVecOps::Iter iter(apTraits, assertions);
5089-
unsigned index = 0;
5090-
while (iter.NextElem(&index))
5147+
if (!BitVecOps::MayBeUninit(assertions))
50915148
{
5092-
AssertionIndex assertionIndex = GetAssertionIndex(index);
5093-
if (assertionIndex > optAssertionCount)
5094-
{
5095-
break;
5096-
}
5097-
AssertionDsc* curAssertion = optGetAssertion(assertionIndex);
5098-
if (!curAssertion->CanPropNonNull())
5099-
{
5100-
continue;
5101-
}
5102-
5103-
if (curAssertion->op1.vn != vn)
5149+
BitVecOps::Iter iter(apTraits, assertions);
5150+
unsigned index = 0;
5151+
while (iter.NextElem(&index))
51045152
{
5105-
continue;
5153+
AssertionDsc* curAssertion = optGetAssertion(GetAssertionIndex(index));
5154+
if (curAssertion->CanPropNonNull() && curAssertion->op1.vn == vn)
5155+
{
5156+
return true;
5157+
}
51065158
}
5107-
5108-
return true;
51095159
}
5110-
51115160
return false;
51125161
}
51135162

@@ -5810,6 +5859,30 @@ ASSERT_VALRET_TP Compiler::optGetVnMappedAssertions(ValueNum vn)
58105859
return BitVecOps::UninitVal();
58115860
}
58125861

5862+
//------------------------------------------------------------------------
5863+
// optGetEdgeAssertions: Given a block and its predecessor, get the assertions
5864+
// the predecessor creates for the block.
5865+
//
5866+
// Arguments:
5867+
// block - The block to get the assertions for.
5868+
// blockPred - The predecessor of the block (creating the assertions).
5869+
//
5870+
// Return Value:
5871+
// The assertions we have about the value number.
5872+
//
5873+
ASSERT_VALRET_TP Compiler::optGetEdgeAssertions(const BasicBlock* block, const BasicBlock* blockPred) const
5874+
{
5875+
if ((blockPred->KindIs(BBJ_COND) && blockPred->TrueTargetIs(block)))
5876+
{
5877+
if (bbJtrueAssertionOut != nullptr)
5878+
{
5879+
return bbJtrueAssertionOut[blockPred->bbNum];
5880+
}
5881+
return BitVecOps::MakeEmpty(apTraits);
5882+
}
5883+
return blockPred->bbAssertionOut;
5884+
}
5885+
58135886
/*****************************************************************************
58145887
*
58155888
* Given a const assertion this method computes the set of implied assertions

src/coreclr/jit/compiler.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8256,6 +8256,14 @@ class Compiler
82568256
bool optNonNullAssertionProp_Ind(ASSERT_VALARG_TP assertions, GenTree* indir);
82578257
bool optWriteBarrierAssertionProp_StoreInd(ASSERT_VALARG_TP assertions, GenTreeStoreInd* indir);
82588258

8259+
enum class AssertVisit
8260+
{
8261+
Continue,
8262+
Abort,
8263+
};
8264+
template <typename TAssertVisitor>
8265+
AssertVisit optVisitReachingAssertions(ValueNum vn, TAssertVisitor argVisitor);
8266+
82598267
void optAssertionProp_RangeProperties(ASSERT_VALARG_TP assertions,
82608268
GenTree* tree,
82618269
bool* isKnownNonZero,
@@ -8275,6 +8283,8 @@ class Compiler
82758283
void optDebugCheckAssertions(AssertionIndex AssertionIndex);
82768284
#endif
82778285

8286+
ASSERT_VALRET_TP optGetEdgeAssertions(const BasicBlock* block, const BasicBlock* blockPred) const;
8287+
82788288
static void optDumpAssertionIndices(const char* header, ASSERT_TP assertions, const char* footer = nullptr);
82798289
static void optDumpAssertionIndices(ASSERT_TP assertions, const char* footer = nullptr);
82808290

src/coreclr/jit/rangecheck.cpp

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -977,26 +977,15 @@ void RangeCheck::MergeAssertion(BasicBlock* block, GenTree* op, Range* pRange DE
977977
ASSERT_TP assertions = BitVecOps::UninitVal();
978978

979979
// If we have a phi arg, we can get to the block from it and use its assertion out.
980-
if (op->gtOper == GT_PHI_ARG)
980+
if (op->OperIs(GT_PHI_ARG))
981981
{
982-
GenTreePhiArg* arg = (GenTreePhiArg*)op;
983-
BasicBlock* pred = arg->gtPredBB;
984-
if (pred->KindIs(BBJ_COND) && pred->FalseTargetIs(block))
982+
const BasicBlock* pred = op->AsPhiArg()->gtPredBB;
983+
assertions = m_pCompiler->optGetEdgeAssertions(block, pred);
984+
if (!BitVecOps::MayBeUninit(assertions))
985985
{
986-
assertions = pred->bbAssertionOut;
987-
JITDUMP("Merge assertions from pred " FMT_BB " edge: ", pred->bbNum);
986+
JITDUMP("Merge assertions created by " FMT_BB " for " FMT_BB "\n", pred->bbNum, block->bbNum);
988987
Compiler::optDumpAssertionIndices(assertions, "\n");
989988
}
990-
else if ((pred->KindIs(BBJ_ALWAYS) && pred->TargetIs(block)) ||
991-
(pred->KindIs(BBJ_COND) && pred->TrueTargetIs(block)))
992-
{
993-
if (m_pCompiler->bbJtrueAssertionOut != nullptr)
994-
{
995-
assertions = m_pCompiler->bbJtrueAssertionOut[pred->bbNum];
996-
JITDUMP("Merge assertions from pred " FMT_BB " JTrue edge: ", pred->bbNum);
997-
Compiler::optDumpAssertionIndices(assertions, "\n");
998-
}
999-
}
1000989
}
1001990
// Get assertions from bbAssertionIn.
1002991
else if (op->IsLocal())

0 commit comments

Comments
 (0)