Skip to content

JIT: enhance SSA checker to check some PHI properties #85533

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

Merged
merged 2 commits into from
Apr 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4200,6 +4200,90 @@ class SsaCheckVisitor : public GenTreeVisitor<SsaCheckVisitor>
void SetBlock(BasicBlock* block)
{
m_block = block;
CheckPhis(block);
}

void CheckPhis(BasicBlock* block)
{
Statement* nonPhiStmt = nullptr;
for (Statement* const stmt : block->Statements())
{
// All PhiDefs should appear before any other statements
//
if (!stmt->IsPhiDefnStmt())
{
if (nonPhiStmt == nullptr)
{
nonPhiStmt = stmt;
}
continue;
}

if (nonPhiStmt != nullptr)
{
SetHasErrors();
JITDUMP("[error] " FMT_BB " PhiDef " FMT_STMT " appears after non-PhiDef " FMT_STMT "\n", block->bbNum,
stmt->GetID(), nonPhiStmt->GetID());
}

GenTree* const phiDefNode = stmt->GetRootNode();
assert(phiDefNode->IsPhiDefn());
GenTreeLclVarCommon* const phiDefLclNode = phiDefNode->gtGetOp1()->AsLclVarCommon();
GenTreePhi* const phi = phiDefNode->gtGetOp2()->AsPhi();

// Verify each GT_PHI_ARG is the right local.
//
// If block does not begin a handler, verify GT_PHI_ARG blocks are unique
// (that is, each pred supplies at most one ssa def).
//
BitVecTraits bitVecTraits(m_compiler->fgBBNumMax + 1, m_compiler);
BitVec phiPreds(BitVecOps::MakeEmpty(&bitVecTraits));

for (GenTreePhi::Use& use : phi->Uses())
{
GenTreePhiArg* const phiArgNode = use.GetNode()->AsPhiArg();
if (phiArgNode->GetLclNum() != phiDefLclNode->GetLclNum())
{
SetHasErrors();
JITDUMP("[error] Wrong local V%02u in PhiArg [%06u] -- expected V%02u\n", phiArgNode->GetLclNum(),
m_compiler->dspTreeID(phiArgNode), phiDefLclNode->GetLclNum());
}

// Handlers can have multiple PhiArgs from the same block and implicit preds.
// So we can't easily check their PhiArgs.
//
if (m_compiler->bbIsHandlerBeg(block))
{
continue;
}

BasicBlock* const phiArgBlock = phiArgNode->gtPredBB;

if (phiArgBlock != nullptr)
{
if (BitVecOps::IsMember(&bitVecTraits, phiPreds, phiArgBlock->bbNum))
{
SetHasErrors();
JITDUMP("[error] " FMT_BB " [%06u]: multiple PhiArgs for predBlock " FMT_BB "\n", block->bbNum,
m_compiler->dspTreeID(phi), phiArgBlock->bbNum);
}

BitVecOps::AddElemD(&bitVecTraits, phiPreds, phiArgBlock->bbNum);

// If phiArgBlock is not a pred of block we either have messed up when building
// SSA or made modifications after building SSA and possibly should have pruned
// out or updated this PhiArg.
//
FlowEdge* const edge = m_compiler->fgGetPredForBlock(block, phiArgBlock);

if (edge == nullptr)
{
JITDUMP("[info] " FMT_BB " [%06u]: stale PhiArg [%06u] pred block " FMT_BB "\n", block->bbNum,
m_compiler->dspTreeID(phi), m_compiler->dspTreeID(phiArgNode), phiArgBlock->bbNum);
}
}
}
}
}

void DoChecks()
Expand Down
13 changes: 9 additions & 4 deletions src/coreclr/jit/redundantbranchopts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,14 @@ bool Compiler::optJumpThreadPhi(BasicBlock* block, GenTree* tree, ValueNum treeN
return false;
}

// Bypass handler blocks, as they can have unusual PHI args.
// In particular multiple SSA defs coming from the same block.
//
if (bbIsHandlerBeg(block))
{
return false;
}

// Find occurrences of phi def VNs in the relop VN.
// We currently just do one level of func destructuring.
//
Expand Down Expand Up @@ -1273,15 +1281,12 @@ bool Compiler::optJumpThreadPhi(BasicBlock* block, GenTree* tree, ValueNum treeN
ValueNum phiArgVN = phiArgNode->GetVN(VNK_Liberal);

// We sometimes see cases where phi args do not have VNs.
// (I recall seeing this before... track down why)
// (VN works in RPO, so PHIs from back edges won't have VNs.
//
if (phiArgVN != ValueNumStore::NoVN)
{
newRelopArgs[i] = phiArgVN;
updatedArg = true;

// paranoia: keep walking uses to make sure no other
// comes from this pred
break;
}
}
Expand Down