@@ -624,24 +624,86 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitor<Substi
624624 {
625625 JITDUMP (" ... found foldable jtrue at [%06u] in " FMT_BB " \n " , m_compiler->dspTreeID (tree),
626626 block->bbNum );
627+ m_compiler->Metrics .InlinerBranchFold ++;
627628
628629 // We have a constant operand, and should have the all clear to optimize.
629630 // Update side effects on the tree, assert there aren't any, and bash to nop.
630631 m_compiler->gtUpdateNodeSideEffects (tree);
631632 assert ((tree->gtFlags & GTF_SIDE_EFFECT) == 0 );
632633 tree->gtBashToNOP ();
633- m_madeChanges = true ;
634+ m_madeChanges = true ;
635+ FlowEdge* removedEdge = nullptr ;
634636
635637 if (condTree->IsIntegralConst (0 ))
636638 {
637- m_compiler->fgRemoveRefPred (block->GetTrueEdge ());
639+ removedEdge = block->GetTrueEdge ();
640+ m_compiler->fgRemoveRefPred (removedEdge);
638641 block->SetKindAndTargetEdge (BBJ_ALWAYS, block->GetFalseEdge ());
639642 }
640643 else
641644 {
642- m_compiler->fgRemoveRefPred (block->GetFalseEdge ());
645+ removedEdge = block->GetFalseEdge ();
646+ m_compiler->fgRemoveRefPred (removedEdge);
643647 block->SetKindAndTargetEdge (BBJ_ALWAYS, block->GetTrueEdge ());
644648 }
649+
650+ // Update profile; make it consistent if possible
651+ //
652+ if (block->hasProfileWeight ())
653+ {
654+ bool repairWasComplete = true ;
655+ weight_t const weight = removedEdge->getLikelyWeight ();
656+
657+ if (weight > 0 )
658+ {
659+ // Target block weight will increase.
660+ //
661+ BasicBlock* const target = block->GetTarget ();
662+ assert (target->hasProfileWeight ());
663+ target->setBBProfileWeight (target->bbWeight + weight);
664+
665+ // Alternate weight will decrease
666+ //
667+ BasicBlock* const alternate = removedEdge->getDestinationBlock ();
668+ assert (alternate->hasProfileWeight ());
669+ weight_t const alternateNewWeight = alternate->bbWeight - weight;
670+
671+ // If profile weights are consistent, expect at worst a slight underflow.
672+ //
673+ if (m_compiler->fgPgoConsistent && (alternateNewWeight < 0 ))
674+ {
675+ assert (m_compiler->fgProfileWeightsEqual (alternateNewWeight, 0 ));
676+ }
677+ alternate->setBBProfileWeight (max (0.0 , alternateNewWeight));
678+
679+ // This will affect profile transitively, so in general
680+ // the profile will become inconsistent.
681+ //
682+ repairWasComplete = false ;
683+
684+ // But we can check for the special case where the
685+ // block's postdominator is target's target (simple
686+ // if/then/else/join).
687+ //
688+ if (target->KindIs (BBJ_ALWAYS))
689+ {
690+ repairWasComplete =
691+ alternate->KindIs (BBJ_ALWAYS) && alternate->TargetIs (target->GetTarget ());
692+ }
693+ }
694+
695+ if (!repairWasComplete)
696+ {
697+ JITDUMP (" Profile data could not be locally repaired. Data %s inconsistent.\n " ,
698+ m_compiler->fgPgoConsistent ? " is now" : " was already" );
699+
700+ if (m_compiler->fgPgoConsistent )
701+ {
702+ m_compiler->Metrics .ProfileInconsistentInlinerBranchFold ++;
703+ m_compiler->fgPgoConsistent = false ;
704+ }
705+ }
706+ }
645707 }
646708 }
647709 else
@@ -692,6 +754,11 @@ PhaseStatus Compiler::fgInline()
692754 JitConfig.JitPrintInlinedMethods ().contains (info.compMethodHnd , info.compClassHnd , &info.compMethodInfo ->args );
693755#endif // DEBUG
694756
757+ if (fgPgoConsistent)
758+ {
759+ Metrics.ProfileConsistentBeforeInline ++;
760+ }
761+
695762 noway_assert (fgFirstBB != nullptr );
696763
697764 BasicBlock* block = fgFirstBB;
@@ -822,6 +889,14 @@ PhaseStatus Compiler::fgInline()
822889 fgRenumberBlocks ();
823890 }
824891
892+ if (fgPgoConsistent)
893+ {
894+ Metrics.ProfileConsistentAfterInline ++;
895+ }
896+
897+ Metrics.InlineCount = m_inlineStrategy->GetInlineCount ();
898+ Metrics.InlineAttempt = m_inlineStrategy->GetImportCount ();
899+
825900 return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING;
826901}
827902
@@ -1611,6 +1686,75 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo)
16111686 }
16121687#endif
16131688
1689+ // Update profile consistency
1690+ //
1691+ // If inlinee is inconsistent, root method will be inconsistent too.
1692+ //
1693+ if (!InlineeCompiler->fgPgoConsistent )
1694+ {
1695+ if (fgPgoConsistent)
1696+ {
1697+ JITDUMP (" INLINER: profile data in root now inconsistent -- inlinee had inconsistency\n " );
1698+ Metrics.ProfileInconsistentInlinee ++;
1699+ fgPgoConsistent = false ;
1700+ }
1701+ }
1702+
1703+ // If we inline a no-return call at a site with profile weight,
1704+ // we will introduce inconsistency.
1705+ //
1706+ if (InlineeCompiler->fgReturnCount == 0 )
1707+ {
1708+ JITDUMP (" INLINER: no-return inlinee\n " );
1709+
1710+ if (iciBlock->bbWeight > 0 )
1711+ {
1712+ if (fgPgoConsistent)
1713+ {
1714+ JITDUMP (" INLINER: profile data in root now inconsistent -- no-return inlinee at call site in " FMT_BB
1715+ " with weight " FMT_WT " \n " ,
1716+ iciBlock->bbNum , iciBlock->bbWeight );
1717+ Metrics.ProfileInconsistentNoReturnInlinee ++;
1718+ fgPgoConsistent = false ;
1719+ }
1720+ }
1721+ else
1722+ {
1723+ // Inlinee scaling should assure this is so.
1724+ //
1725+ assert (InlineeCompiler->fgFirstBB ->bbWeight == 0 );
1726+ }
1727+ }
1728+
1729+ // If the call site is not in a try and the callee has a throw,
1730+ // we may introduce inconsistency.
1731+ //
1732+ // Technically we should check if the callee has a throw not in a try, but since
1733+ // we can't inline methods with EH yet we don't see those.
1734+ //
1735+ if (InlineeCompiler->fgThrowCount > 0 )
1736+ {
1737+ JITDUMP (" INLINER: may-throw inlinee\n " );
1738+
1739+ if (iciBlock->bbWeight > 0 )
1740+ {
1741+ if (fgPgoConsistent)
1742+ {
1743+ JITDUMP (" INLINER: profile data in root now inconsistent -- may-throw inlinee at call site in " FMT_BB
1744+ " with weight " FMT_WT " \n " ,
1745+ iciBlock->bbNum , iciBlock->bbWeight );
1746+ Metrics.ProfileInconsistentMayThrowInlinee ++;
1747+ fgPgoConsistent = false ;
1748+ }
1749+ }
1750+ else
1751+ {
1752+ // Inlinee scaling should assure this is so.
1753+ //
1754+ assert (InlineeCompiler->fgFirstBB ->bbWeight == 0 );
1755+ }
1756+ }
1757+
16141758 // If an inlinee needs GS cookie we need to make sure that the cookie will not be allocated at zero stack offset.
16151759 // Note that if the root method needs GS cookie then this has already been taken care of.
16161760 if (!getNeedsGSSecurityCookie () && InlineeCompiler->getNeedsGSSecurityCookie ())
0 commit comments