Skip to content

Commit 1edd69c

Browse files
authored
JIT: fix issue in assertion prop phi inference (#116326)
Check if there are block preds that are not in phi preds if initial phi-inference succeeds. Also add the ability to dump the flow graph via hash. Fixes #116212.
1 parent 7ed5ede commit 1edd69c

File tree

5 files changed

+90
-7
lines changed

5 files changed

+90
-7
lines changed

src/coreclr/jit/assertionprop.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4285,6 +4285,11 @@ Compiler::AssertVisit Compiler::optVisitReachingAssertions(ValueNum vn, TAssertV
42854285
GenTreeLclVarCommon* node = ssaDef->GetDefNode();
42864286
assert(node->IsPhiDefn());
42874287

4288+
// Keep track of the set of phi-preds
4289+
//
4290+
BitVecTraits traits(fgBBNumMax + 1, this);
4291+
BitVec visitedBlocks = BitVecOps::MakeEmpty(&traits);
4292+
42884293
for (GenTreePhi::Use& use : node->Data()->AsPhi()->Uses())
42894294
{
42904295
GenTreePhiArg* phiArg = use.GetNode()->AsPhiArg();
@@ -4295,6 +4300,22 @@ Compiler::AssertVisit Compiler::optVisitReachingAssertions(ValueNum vn, TAssertV
42954300
// The visitor wants to abort the walk.
42964301
return AssertVisit::Abort;
42974302
}
4303+
BitVecOps::AddElemD(&traits, visitedBlocks, phiArg->gtPredBB->bbNum);
4304+
}
4305+
4306+
// Verify the set of phi-preds covers the set of block preds
4307+
//
4308+
for (BasicBlock* const pred : ssaDef->GetBlock()->PredBlocks())
4309+
{
4310+
if (!BitVecOps::IsMember(&traits, visitedBlocks, pred->bbNum))
4311+
{
4312+
JITDUMP("... optVisitReachingAssertions in " FMT_BB ": pred " FMT_BB " not a phi-pred\n",
4313+
ssaDef->GetBlock()->bbNum, pred->bbNum);
4314+
4315+
// We missed examining a block pred. Fail the phi inference.
4316+
//
4317+
return AssertVisit::Abort;
4318+
}
42984319
}
42994320
return AssertVisit::Continue;
43004321
}
@@ -4431,6 +4452,7 @@ GenTree* Compiler::optAssertionPropGlobal_RelOp(ASSERT_VALARG_TP assertions,
44314452
// and if all of them are known to be non-null, we can bash the comparison to true/false.
44324453
if (op2->IsIntegralConst(0) && op1->TypeIs(TYP_REF))
44334454
{
4455+
JITDUMP("Checking PHI [%06u] arguments for non-nullness\n", dspTreeID(op1))
44344456
auto visitor = [this](ValueNum reachingVN, ASSERT_TP reachingAssertions) {
44354457
return optAssertionVNIsNonNull(reachingVN, reachingAssertions) ? AssertVisit::Continue : AssertVisit::Abort;
44364458
};
@@ -5005,6 +5027,7 @@ bool Compiler::optAssertionVNIsNonNull(ValueNum vn, ASSERT_VALARG_TP assertions)
50055027
}
50065028
}
50075029
}
5030+
50085031
return false;
50095032
}
50105033

src/coreclr/jit/fgdiagnostic.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,12 @@ FILE* Compiler::fgOpenFlowGraphFile(bool* wbDontClose, Phases phase, PhasePositi
409409

410410
#ifdef DEBUG
411411
dumpFunction = JitConfig.JitDumpFg().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args);
412+
dumpFunction |= ((unsigned)JitConfig.JitDumpFgHash() == info.compMethodHash());
413+
414+
if (opts.IsTier0())
415+
{
416+
dumpFunction &= (JitConfig.JitDumpFgTier0() > 0);
417+
}
412418

413419
CompAllocator allocator = getAllocatorDebugOnly();
414420
filename = JitConfig.JitDumpFgFile();
@@ -656,6 +662,8 @@ FILE* Compiler::fgOpenFlowGraphFile(bool* wbDontClose, Phases phase, PhasePositi
656662
// Here are the config values that control it:
657663
// DOTNET_JitDumpFg A string (ala the DOTNET_JitDump string) indicating what methods to dump
658664
// flowgraphs for.
665+
// DOTNET_JitDumpFgHash Dump flowgraphs for methods with this hash
666+
// DOTNET_JitDumpFgTier0 Dump tier-0 compilations
659667
// DOTNET_JitDumpFgDir A path to a directory into which the flowgraphs will be dumped.
660668
// DOTNET_JitDumpFgFile The filename to use. The default is "default.[xml|dot]".
661669
// Note that the new graphs will be appended to this file if it already exists.

src/coreclr/jit/jitconfigvalues.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -262,13 +262,16 @@ CONFIG_METHODSET(JitUnwindDump, "JitUnwindDump") // Dump the unwind codes for th
262262
// JitDumpFg - dump flowgraph
263263
//
264264

265-
CONFIG_METHODSET(JitDumpFg, "JitDumpFg") // Dumps Xml/Dot Flowgraph for specified method
266-
CONFIG_STRING(JitDumpFgDir, "JitDumpFgDir") // Directory for Xml/Dot flowgraph dump(s)
267-
CONFIG_STRING(JitDumpFgFile, "JitDumpFgFile") // Filename for Xml/Dot flowgraph dump(s) (default: "default")
268-
CONFIG_STRING(JitDumpFgPhase, "JitDumpFgPhase") // Phase-based Xml/Dot flowgraph support. Set to the short name of a
269-
// phase to see the flowgraph after that phase. Leave unset to dump
270-
// after COLD-BLK (determine first cold block) or set to * for all
271-
// phases
265+
CONFIG_METHODSET(JitDumpFg, "JitDumpFg") // Dumps Xml/Dot Flowgraph for specified method
266+
CONFIG_INTEGER(JitDumpFgHash, "JitDumpFgHash", 0) // Dumps Xml/Dot Flowgraph for specified method
267+
CONFIG_INTEGER(JitDumpFgTier0, "JitDumpFgTier0", 1) // Dumps Xml/Dot Flowgraph for tier-0 compilations of specified
268+
// methods
269+
CONFIG_STRING(JitDumpFgDir, "JitDumpFgDir") // Directory for Xml/Dot flowgraph dump(s)
270+
CONFIG_STRING(JitDumpFgFile, "JitDumpFgFile") // Filename for Xml/Dot flowgraph dump(s) (default: "default")
271+
CONFIG_STRING(JitDumpFgPhase, "JitDumpFgPhase") // Phase-based Xml/Dot flowgraph support. Set to the short name of a
272+
// phase to see the flowgraph after that phase. Leave unset to dump
273+
// after COLD-BLK (determine first cold block) or set to * for all
274+
// phases
272275
CONFIG_STRING(JitDumpFgPrePhase, "JitDumpFgPrePhase") // Same as JitDumpFgPhase, but specifies to dump pre-phase, not
273276
// post-phase.
274277
CONFIG_INTEGER(JitDumpFgDot, "JitDumpFgDot", 1) // 0 == dump XML format; non-zero == dump DOT format
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Runtime.CompilerServices;
5+
using Xunit;
6+
7+
public class Runtime_116212
8+
{
9+
[Fact]
10+
public static void Test() => Problem(1, null);
11+
12+
[MethodImpl(MethodImplOptions.NoInlining)]
13+
static void SideEffect(int x = 0) { }
14+
15+
[MethodImpl(MethodImplOptions.NoInlining)]
16+
static bool Problem(int a, object? o)
17+
{
18+
if (o == null)
19+
{
20+
if (a == 0)
21+
{
22+
SideEffect();
23+
}
24+
25+
if (a == 0)
26+
{
27+
o = new object();
28+
}
29+
}
30+
31+
object? oo = o;
32+
33+
if (oo != null)
34+
{
35+
SideEffect(oo.GetHashCode());
36+
return true;
37+
}
38+
39+
return false;
40+
}
41+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<Optimize>True</Optimize>
4+
</PropertyGroup>
5+
<ItemGroup>
6+
<Compile Include="$(MSBuildProjectName).cs" />
7+
</ItemGroup>
8+
</Project>

0 commit comments

Comments
 (0)