Skip to content

Commit e482bba

Browse files
authored
JIT: enhance SSA checker to check some PHI properties (#85533)
* Ensure PhiArgs have the right local number. * Ensure PhiArgs have unique `gtPredBB` (for most blocks) * Verify phidef/non-phidef order Handler entries may have multiple PhiArgs with the same `gtPredBB`. This is by design, to model exceptional flow from the middle of the block to a handler. Jump threading relies on there being just a single PhiArg per pred. So exclude handler entry blocks from jump threading. Prep work for possibly changing SSA to produce one PhiArg per pred, instead of one PhiArg per ssa def.
1 parent 80ff9ee commit e482bba

File tree

2 files changed

+93
-4
lines changed

2 files changed

+93
-4
lines changed

src/coreclr/jit/fgdiagnostic.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4200,6 +4200,90 @@ class SsaCheckVisitor : public GenTreeVisitor<SsaCheckVisitor>
42004200
void SetBlock(BasicBlock* block)
42014201
{
42024202
m_block = block;
4203+
CheckPhis(block);
4204+
}
4205+
4206+
void CheckPhis(BasicBlock* block)
4207+
{
4208+
Statement* nonPhiStmt = nullptr;
4209+
for (Statement* const stmt : block->Statements())
4210+
{
4211+
// All PhiDefs should appear before any other statements
4212+
//
4213+
if (!stmt->IsPhiDefnStmt())
4214+
{
4215+
if (nonPhiStmt == nullptr)
4216+
{
4217+
nonPhiStmt = stmt;
4218+
}
4219+
continue;
4220+
}
4221+
4222+
if (nonPhiStmt != nullptr)
4223+
{
4224+
SetHasErrors();
4225+
JITDUMP("[error] " FMT_BB " PhiDef " FMT_STMT " appears after non-PhiDef " FMT_STMT "\n", block->bbNum,
4226+
stmt->GetID(), nonPhiStmt->GetID());
4227+
}
4228+
4229+
GenTree* const phiDefNode = stmt->GetRootNode();
4230+
assert(phiDefNode->IsPhiDefn());
4231+
GenTreeLclVarCommon* const phiDefLclNode = phiDefNode->gtGetOp1()->AsLclVarCommon();
4232+
GenTreePhi* const phi = phiDefNode->gtGetOp2()->AsPhi();
4233+
4234+
// Verify each GT_PHI_ARG is the right local.
4235+
//
4236+
// If block does not begin a handler, verify GT_PHI_ARG blocks are unique
4237+
// (that is, each pred supplies at most one ssa def).
4238+
//
4239+
BitVecTraits bitVecTraits(m_compiler->fgBBNumMax + 1, m_compiler);
4240+
BitVec phiPreds(BitVecOps::MakeEmpty(&bitVecTraits));
4241+
4242+
for (GenTreePhi::Use& use : phi->Uses())
4243+
{
4244+
GenTreePhiArg* const phiArgNode = use.GetNode()->AsPhiArg();
4245+
if (phiArgNode->GetLclNum() != phiDefLclNode->GetLclNum())
4246+
{
4247+
SetHasErrors();
4248+
JITDUMP("[error] Wrong local V%02u in PhiArg [%06u] -- expected V%02u\n", phiArgNode->GetLclNum(),
4249+
m_compiler->dspTreeID(phiArgNode), phiDefLclNode->GetLclNum());
4250+
}
4251+
4252+
// Handlers can have multiple PhiArgs from the same block and implicit preds.
4253+
// So we can't easily check their PhiArgs.
4254+
//
4255+
if (m_compiler->bbIsHandlerBeg(block))
4256+
{
4257+
continue;
4258+
}
4259+
4260+
BasicBlock* const phiArgBlock = phiArgNode->gtPredBB;
4261+
4262+
if (phiArgBlock != nullptr)
4263+
{
4264+
if (BitVecOps::IsMember(&bitVecTraits, phiPreds, phiArgBlock->bbNum))
4265+
{
4266+
SetHasErrors();
4267+
JITDUMP("[error] " FMT_BB " [%06u]: multiple PhiArgs for predBlock " FMT_BB "\n", block->bbNum,
4268+
m_compiler->dspTreeID(phi), phiArgBlock->bbNum);
4269+
}
4270+
4271+
BitVecOps::AddElemD(&bitVecTraits, phiPreds, phiArgBlock->bbNum);
4272+
4273+
// If phiArgBlock is not a pred of block we either have messed up when building
4274+
// SSA or made modifications after building SSA and possibly should have pruned
4275+
// out or updated this PhiArg.
4276+
//
4277+
FlowEdge* const edge = m_compiler->fgGetPredForBlock(block, phiArgBlock);
4278+
4279+
if (edge == nullptr)
4280+
{
4281+
JITDUMP("[info] " FMT_BB " [%06u]: stale PhiArg [%06u] pred block " FMT_BB "\n", block->bbNum,
4282+
m_compiler->dspTreeID(phi), m_compiler->dspTreeID(phiArgNode), phiArgBlock->bbNum);
4283+
}
4284+
}
4285+
}
4286+
}
42034287
}
42044288

42054289
void DoChecks()

src/coreclr/jit/redundantbranchopts.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,14 @@ bool Compiler::optJumpThreadPhi(BasicBlock* block, GenTree* tree, ValueNum treeN
11671167
return false;
11681168
}
11691169

1170+
// Bypass handler blocks, as they can have unusual PHI args.
1171+
// In particular multiple SSA defs coming from the same block.
1172+
//
1173+
if (bbIsHandlerBeg(block))
1174+
{
1175+
return false;
1176+
}
1177+
11701178
// Find occurrences of phi def VNs in the relop VN.
11711179
// We currently just do one level of func destructuring.
11721180
//
@@ -1273,15 +1281,12 @@ bool Compiler::optJumpThreadPhi(BasicBlock* block, GenTree* tree, ValueNum treeN
12731281
ValueNum phiArgVN = phiArgNode->GetVN(VNK_Liberal);
12741282

12751283
// We sometimes see cases where phi args do not have VNs.
1276-
// (I recall seeing this before... track down why)
1284+
// (VN works in RPO, so PHIs from back edges won't have VNs.
12771285
//
12781286
if (phiArgVN != ValueNumStore::NoVN)
12791287
{
12801288
newRelopArgs[i] = phiArgVN;
12811289
updatedArg = true;
1282-
1283-
// paranoia: keep walking uses to make sure no other
1284-
// comes from this pred
12851290
break;
12861291
}
12871292
}

0 commit comments

Comments
 (0)