Skip to content
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
107 changes: 101 additions & 6 deletions src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4296,10 +4296,34 @@ Compiler::AssertVisit Compiler::optVisitReachingAssertions(ValueNum vn, TAssertV
//
BitVecTraits traits(fgBBNumMax + 1, this);
BitVec visitedBlocks = BitVecOps::MakeEmpty(&traits);
BitVec actualPreds = BitVecOps::MakeEmpty(&traits);

// Given an ssaDef and its block, we must consider two edge cases:
// 1) ssaDef->GetBlock()->PredBlocks() contains blocks that do not exist in AsPhi()->Uses()
// 2) AsPhi()->Uses() contains blocks that do not exist in ssaDef->GetBlock()->PredBlocks()
//
// We conservatively terminate the walk if either mismatch occurs.
//
for (BasicBlock* const pred : ssaDef->GetBlock()->PredBlocks())
{
BitVecOps::AddElemD(&traits, actualPreds, pred->bbNum);
}

for (GenTreePhi::Use& use : node->Data()->AsPhi()->Uses())
{
GenTreePhiArg* phiArg = use.GetNode()->AsPhiArg();
GenTreePhiArg* phiArg = use.GetNode()->AsPhiArg();

if (!BitVecOps::IsMember(&traits, actualPreds, phiArg->gtPredBB->bbNum))
{
JITDUMP("... optVisitReachingAssertions in " FMT_BB ": phi-pred " FMT_BB " not a block pred\n",
ssaDef->GetBlock()->bbNum, phiArg->gtPredBB->bbNum);

// We probably can just ignore this phi-pred if we know for sure phiArg->gtPredBB never reaches
// the ssaDef's block. For now, conservatively fail the phi inference in this case.
// Alternatively, we can request optRepeat here.
return AssertVisit::Abort;
}

const ValueNum phiArgVN = vnStore->VNConservativeNormalValue(phiArg->gtVNPair);
ASSERT_TP assertions = optGetEdgeAssertions(ssaDef->GetBlock(), phiArg->gtPredBB);
if (argVisitor(phiArgVN, assertions) == AssertVisit::Abort)
Expand All @@ -4312,6 +4336,8 @@ Compiler::AssertVisit Compiler::optVisitReachingAssertions(ValueNum vn, TAssertV

// Verify the set of phi-preds covers the set of block preds
//
// We can just do BitVecOps::Equal(&traits, visitedBlocks, actualPreds), but
// re-iterating the preds is cheaper.
for (BasicBlock* const pred : ssaDef->GetBlock()->PredBlocks())
{
if (!BitVecOps::IsMember(&traits, visitedBlocks, pred->bbNum))
Expand Down Expand Up @@ -5876,15 +5902,36 @@ ASSERT_VALRET_TP Compiler::optGetVnMappedAssertions(ValueNum vn)
//
ASSERT_VALRET_TP Compiler::optGetEdgeAssertions(const BasicBlock* block, const BasicBlock* blockPred) const
{
if ((blockPred->KindIs(BBJ_COND) && blockPred->TrueTargetIs(block)))
assert(block != nullptr);
if (blockPred->KindIs(BBJ_COND))
{
if (blockPred->TrueTargetIs(block))
{
if (bbJtrueAssertionOut != nullptr)
{
return bbJtrueAssertionOut[blockPred->bbNum];
}
return BitVecOps::MakeEmpty(apTraits);
}

// If block is not the false target either, the edge doesn't exist
// (e.g. a stale PHI arg pred after edge redirection by RBO).
// Return empty to avoid using assertions from an unrelated edge.
if (!blockPred->FalseTargetIs(block))
{
return BitVecOps::MakeEmpty(apTraits);
}
return blockPred->bbAssertionOut;
}

for (BasicBlock* const pred : block->PredBlocks())
{
if (bbJtrueAssertionOut != nullptr)
if (pred == blockPred)
{
return bbJtrueAssertionOut[blockPred->bbNum];
return blockPred->bbAssertionOut;
}
return BitVecOps::MakeEmpty(apTraits);
}
return blockPred->bbAssertionOut;
return BitVecOps::MakeEmpty(apTraits);
}

/*****************************************************************************
Expand Down Expand Up @@ -6770,9 +6817,57 @@ PhaseStatus Compiler::optAssertionPropMain()
printf("\n");
}
#endif
// Record BBJ_COND state before morphing so we can fix up
// assertion out sets if morph changes the block's edges.
bool wasCond = block->KindIs(BBJ_COND);
BasicBlock* trueBb = wasCond ? block->GetTrueTarget() : nullptr;
BasicBlock* falseBb = wasCond ? block->GetFalseTarget() : nullptr;

// Re-morph the statement.
fgMorphBlockStmt(block, stmt DEBUGARG("optAssertionPropMain"));
madeChanges = true;

// Fix up assertion out sets if morphing changed the block's edges in a way
// that affects the semantics of the assertions.
//
if (wasCond && !optLocalAssertionProp)
{
if (!block->KindIs(BBJ_COND))
{
// NOTE: if trueBb == falseBb then we don't know how assertions may change
// so we take the conservative path in that case.
//
if ((block->GetUniqueSucc() == trueBb) && (trueBb != falseBb))
{
// BBJ_COND was folded (e.g. to BBJ_ALWAYS).
// Fix up bbAssertionOut to match the retained edge.
//
BitVecOps::Assign(apTraits, block->bbAssertionOut, bbJtrueAssertionOut[block->bbNum]);
}
else if ((block->GetUniqueSucc() == falseBb) && (trueBb != falseBb))
{
// bbAssertionOut already has the false-edge assertions.
}
else
{
// Converted to something unexpected (e.g. BBJ_SWITCH) — conservatively
// just propagate the IN assertions, which is better than losing all assertions.
//
BitVecOps::Assign(apTraits, block->bbAssertionOut, block->bbAssertionIn);
// We can also quickly walk over the trees and accumulate more assertions if needed.
// NOTE: this is not valid for LocalAP as assertions may die in the middle of the block
}
}
else if ((block->GetTrueTarget() != trueBb) || (block->GetFalseTarget() != falseBb))
{
// Conservatively clear assertions if edges changed in any way that we don't expect.
// We still can be smart here and handle e.g. edge flip.
//
BitVecOps::Assign(apTraits, block->bbAssertionOut, block->bbAssertionIn);
BitVecOps::Assign(apTraits, bbJtrueAssertionOut[block->bbNum], block->bbAssertionIn);
// NOTE: this is not valid for LocalAP as assertions may die in the middle of the block
}
}
}

// Check if propagation removed statements starting from current stmt.
Expand Down
40 changes: 40 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_124507/Runtime_124507.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// Generated by Fuzzlyn v3.3 on 2026-02-28 17:41:56
// Run on Arm Linux
// Seed: 4947552585052090314
// Reduced from 335.1 KiB to 0.5 KiB in 00:04:35
// Debug: Prints 0 line(s)
// Release: Prints 1 line(s)

using System;
using System.Runtime.CompilerServices;
using Xunit;

public class Runtime_124507
{
[Fact]
public static void TestEntryPoint()
{
DeadPhiAssertionProp(0);
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void DeadPhiAssertionProp(ushort F7)
{
bool var0 = 1 == F7;
bool var15 = var0;
if (var0)
{
var0 = true;
}
if (!var15)
{
if (var0)
{
throw new InvalidOperationException();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
</Project>
Loading