@@ -116,6 +116,11 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
116116 return false ;
117117 }
118118
119+ const ValueNumStore::VN_RELATION_KIND vnRelations[] = {ValueNumStore::VN_RELATION_KIND::VRK_Same,
120+ ValueNumStore::VN_RELATION_KIND::VRK_Reverse,
121+ ValueNumStore::VN_RELATION_KIND::VRK_Swap,
122+ ValueNumStore::VN_RELATION_KIND::VRK_SwapReverse};
123+
119124 while (domBlock != nullptr )
120125 {
121126 // Check the current dominator
@@ -134,21 +139,21 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
134139 //
135140 // Look for an exact match and also try the various swapped/reversed forms.
136141 //
137- const ValueNum treeVN = tree->GetVN (VNK_Liberal);
138- const ValueNum domCmpVN = domCmpTree->GetVN (VNK_Liberal);
139- const ValueNum domCmpSwpVN =
140- vnStore-> GetRelatedRelop (domCmpVN, ValueNumStore::VN_RELATION_KIND::VRK_Swap) ;
141- const ValueNum domCmpRevVN =
142- vnStore-> GetRelatedRelop (domCmpVN, ValueNumStore::VN_RELATION_KIND::VRK_Reverse);
143- const ValueNum domCmpSwpRevVN =
144- vnStore->GetRelatedRelop (domCmpVN, ValueNumStore::VN_RELATION_KIND::VRK_SwapReverse );
145-
146- const bool matchCmp = ((domCmpVN != ValueNumStore::NoVN) && (domCmpVN == treeVN));
147- const bool matchSwp = ((domCmpSwpVN != ValueNumStore::NoVN) && (domCmpSwpVN == treeVN)) ;
148- const bool matchRev = ((domCmpRevVN != ValueNumStore::NoVN) && (domCmpRevVN == treeVN)) ;
149- const bool matchSwpRev = ((domCmpSwpRevVN != ValueNumStore::NoVN) && (domCmpSwpRevVN == treeVN)) ;
150- const bool domIsSameRelop = matchCmp || matchSwp;
151- const bool domIsRevRelop = matchRev || matchSwpRev;
142+ const ValueNum treeVN = tree->GetVN (VNK_Liberal);
143+ const ValueNum domCmpVN = domCmpTree->GetVN (VNK_Liberal);
144+ ValueNumStore::VN_RELATION_KIND vnRelationMatch = ValueNumStore::VN_RELATION_KIND::VRK_Same;
145+ bool matched = false ;
146+
147+ for ( auto vnRelation : vnRelations)
148+ {
149+ const ValueNum relatedVN = vnStore->GetRelatedRelop (domCmpVN, vnRelation );
150+ if ((relatedVN != ValueNumStore::NoVN) && (relatedVN == treeVN))
151+ {
152+ vnRelationMatch = vnRelation ;
153+ matched = true ;
154+ break ;
155+ }
156+ }
152157
153158 // Note we could also infer the tree relop's value from relops higher in the dom tree
154159 // that involve the same operands but are not swaps or reverses.
@@ -157,18 +162,20 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
157162 //
158163 // That is left as a future enhancement.
159164 //
160- if (domIsSameRelop || domIsRevRelop )
165+ if (matched )
161166 {
162167 // The compare in "tree" is redundant.
163168 // Is there a unique path from the dominating compare?
164169 //
165- JITDUMP (" \n Dominator " FMT_BB " of " FMT_BB " has %srelop with %s liberal VN\n " , domBlock->bbNum ,
166- block->bbNum , matchSwp || matchSwpRev ? " swapped " : " " ,
167- domIsSameRelop ? " the same" : " a reverse" );
170+ JITDUMP (" \n Dominator " FMT_BB " of " FMT_BB " has relop with %s liberal VN\n " , domBlock->bbNum ,
171+ block->bbNum , ValueNumStore::VNRelationString (vnRelationMatch));
168172 DISPTREE (domCmpTree);
169173 JITDUMP (" Redundant compare; current relop:\n " );
170174 DISPTREE (tree);
171175
176+ const bool domIsSameRelop = (vnRelationMatch == ValueNumStore::VN_RELATION_KIND::VRK_Same) ||
177+ (vnRelationMatch == ValueNumStore::VN_RELATION_KIND::VRK_Swap);
178+
172179 BasicBlock* const trueSuccessor = domBlock->bbJumpDest ;
173180 BasicBlock* const falseSuccessor = domBlock->bbNext ;
174181 const bool trueReaches = optReachable (trueSuccessor, block, domBlock);
@@ -787,6 +794,14 @@ bool Compiler::optRedundantRelop(BasicBlock* const block)
787794 return false ;
788795 }
789796
797+ // If there's just one statement, bail.
798+ //
799+ if (stmt == block->firstStmt ())
800+ {
801+ JITDUMP (" -- no, no prior stmt\n " );
802+ return false ;
803+ }
804+
790805 GenTree* const jumpTree = stmt->GetRootNode ();
791806
792807 if (!jumpTree->OperIs (GT_JTRUE))
@@ -813,21 +828,17 @@ bool Compiler::optRedundantRelop(BasicBlock* const block)
813828
814829 // If relop's value is known, bail.
815830 //
816- const ValueNum treeVN = tree->GetVN (VNK_Liberal);
831+ const ValueNum treeVN = vnStore-> VNNormalValue ( tree->GetVN (VNK_Liberal) );
817832
818833 if (vnStore->IsVNConstant (treeVN))
819834 {
820835 JITDUMP (" -- no, jump tree cond is constant\n " );
821836 return false ;
822837 }
823838
824- // If there 's just one statement, bail .
839+ // Save off the jump tree 's liberal exceptional VN .
825840 //
826- if (stmt == block->firstStmt ())
827- {
828- JITDUMP (" -- no, no prior stmt\n " );
829- return false ;
830- }
841+ const ValueNum treeExcVN = vnStore->VNExceptionSet (tree->GetVN (VNK_Liberal));
831842
832843 JITDUMP (" \n optRedundantRelop in " FMT_BB " ; jump tree is\n " , block->bbNum );
833844 DISPTREE (jumpTree);
@@ -836,9 +847,16 @@ bool Compiler::optRedundantRelop(BasicBlock* const block)
836847 // * makes the current relop redundant;
837848 // * can safely and profitably forward substituted to the jump.
838849 //
839- Statement* prevStmt = stmt;
840- GenTree* candidateTree = nullptr ;
841- bool reverse = false ;
850+ Statement* prevStmt = stmt;
851+ GenTree* candidateTree = nullptr ;
852+ Statement* candidateStmt = nullptr ;
853+ ValueNumStore::VN_RELATION_KIND vnRelationMatch = ValueNumStore::VN_RELATION_KIND::VRK_Same;
854+ bool sideEffect = false ;
855+
856+ const ValueNumStore::VN_RELATION_KIND vnRelations[] = {ValueNumStore::VN_RELATION_KIND::VRK_Same,
857+ ValueNumStore::VN_RELATION_KIND::VRK_Reverse,
858+ ValueNumStore::VN_RELATION_KIND::VRK_Swap,
859+ ValueNumStore::VN_RELATION_KIND::VRK_SwapReverse};
842860
843861 // We need to keep track of which locals might be killed by
844862 // the trees between the expression we want to forward substitute
@@ -858,6 +876,13 @@ bool Compiler::optRedundantRelop(BasicBlock* const block)
858876
859877 while (true )
860878 {
879+ // If we've run a cross a side effecting pred tree, stop looking.
880+ //
881+ if (sideEffect)
882+ {
883+ break ;
884+ }
885+
861886 prevStmt = prevStmt->GetPrevStmt ();
862887
863888 // Backwards statement walks wrap around, so if we get
@@ -882,12 +907,22 @@ bool Compiler::optRedundantRelop(BasicBlock* const block)
882907 continue ;
883908 }
884909
885- // If prevTree has side effects other than GTF_EXCEPT or GTF_ASG, bail.
910+ // If prevTree has side effects other than GTF_EXCEPT or GTF_ASG, bail,
911+ // unless it is in the immediately preceeding statement.
912+ //
913+ // (we'll later show that any exception must come from the RHS as the LHS
914+ // will be a simple local).
886915 //
887916 if ((prevTree->gtFlags & GTF_SIDE_EFFECT) != (prevTree->gtFlags & (GTF_EXCEPT | GTF_ASG)))
888917 {
889- JITDUMP (" -- prev tree has side effects\n " );
890- break ;
918+ if (prevStmt->GetNextStmt () != stmt)
919+ {
920+ JITDUMP (" -- prev tree has side effects and is not next to jumpTree\n " );
921+ break ;
922+ }
923+
924+ JITDUMP (" -- prev tree has side effects, allowing as prev tree is immediately before jumpTree\n " );
925+ sideEffect = true ;
891926 }
892927
893928 if (!prevTree->OperIs (GT_ASG))
@@ -946,46 +981,43 @@ bool Compiler::optRedundantRelop(BasicBlock* const block)
946981
947982 definedLocals[definedLocalsCount++] = prevTreeLcl;
948983
949- // If the VN of RHS is the VN of the current tree, or is "related", consider forward sub.
950- //
951- // Todo: generalize to allow when normal VN of RHS == normal VN of tree, and RHS except set contains tree except
952- // set.
953- // The concern here is whether can we forward sub an excepting (but otherwise non-side effecting tree) and
954- // guarantee
955- // the same side effect behavior.
956- //
957- // A common case we see is that there's a compare of an array element guarded by a bounds check,
958- // so prevTree is something like:
984+ // If the normal liberal VN of RHS is the normal liberal VN of the current tree, or is "related",
985+ // consider forward sub.
959986 //
960- // (ASG lcl, (COMMA (bds-check), (NE (array-indir), ...)))
961- //
962- // This may have the same normal VN as our tree, but also has an exception set.
963- //
964- // Another common pattern is that the prevTree contains a call.
965- //
966- const ValueNum domCmpVN = prevTreeRHS->GetVN (VNK_Liberal);
967- const ValueNum domCmpSwpVN = vnStore->GetRelatedRelop (domCmpVN, ValueNumStore::VN_RELATION_KIND::VRK_Swap);
968- const ValueNum domCmpRevVN = vnStore->GetRelatedRelop (domCmpVN, ValueNumStore::VN_RELATION_KIND::VRK_Reverse);
969- const ValueNum domCmpSwpRevVN =
970- vnStore->GetRelatedRelop (domCmpVN, ValueNumStore::VN_RELATION_KIND::VRK_SwapReverse);
971-
972- const bool matchCmp = ((domCmpVN != ValueNumStore::NoVN) && (domCmpVN == treeVN));
973- const bool matchSwp = ((domCmpSwpVN != ValueNumStore::NoVN) && (domCmpSwpVN == treeVN));
974- const bool matchRev = ((domCmpRevVN != ValueNumStore::NoVN) && (domCmpRevVN == treeVN));
975- const bool matchSwpRev = ((domCmpSwpRevVN != ValueNumStore::NoVN) && (domCmpSwpRevVN == treeVN));
976- const bool domIsSameRelop = matchCmp || matchSwp;
977- const bool domIsRevRelop = matchRev || matchSwpRev;
978-
979- // If the tree is some unrelated computation, keep looking farther up.
980- //
981- if (!domIsSameRelop && !domIsRevRelop)
987+ const ValueNum domCmpVN = vnStore->VNNormalValue (prevTreeRHS->GetVN (VNK_Liberal));
988+ bool matched = false ;
989+
990+ for (auto vnRelation : vnRelations)
991+ {
992+ const ValueNum relatedVN = vnStore->GetRelatedRelop (domCmpVN, vnRelation);
993+ if ((relatedVN != ValueNumStore::NoVN) && (relatedVN == treeVN))
994+ {
995+ vnRelationMatch = vnRelation;
996+ matched = true ;
997+ break ;
998+ }
999+ }
1000+
1001+ if (!matched)
9821002 {
9831003 JITDUMP (" -- prev tree VN is not related\n " );
9841004 continue ;
9851005 }
9861006
987- JITDUMP (" -- prev tree has %srelop with %s liberal VN\n " , matchSwp || matchSwpRev ? " swapped " : " " ,
988- domIsSameRelop ? " the same" : " a reverse" );
1007+ JITDUMP (" -- prev tree has relop with %s liberal VN\n " , ValueNumStore::VNRelationString (vnRelationMatch));
1008+
1009+ // If the jump tree VN has exceptions, verify that the RHS tree has a superset.
1010+ //
1011+ if (treeExcVN != vnStore->VNForEmptyExcSet ())
1012+ {
1013+ const ValueNum prevTreeExcVN = vnStore->VNExceptionSet (prevTreeRHS->GetVN (VNK_Liberal));
1014+
1015+ if (!vnStore->VNExcIsSubset (prevTreeExcVN, treeExcVN))
1016+ {
1017+ JITDUMP (" -- prev tree does not anticipate all jump tree exceptions\n " );
1018+ break ;
1019+ }
1020+ }
9891021
9901022 // See if we can safely move a copy of prevTreeRHS later, to replace tree.
9911023 // We can, if none of its lcls are killed.
@@ -1015,9 +1047,9 @@ bool Compiler::optRedundantRelop(BasicBlock* const block)
10151047 continue ;
10161048 }
10171049
1018- // Heuristic: if the lcl defined here is live out, forward sub
1019- // won't result in the original becoming dead. If the local is not
1020- // live out that still may happen, but it's less likely .
1050+ // If the lcl defined here is live out, forward sub is problematic.
1051+ // We'll either create a redundant tree (as the original won't be dead)
1052+ // or lose the def (if we actually move the RHS tree) .
10211053 //
10221054 if (VarSetOps::IsMember (this , block->bbLiveOut , prevTreeLclDsc->lvVarIndex ))
10231055 {
@@ -1027,35 +1059,62 @@ bool Compiler::optRedundantRelop(BasicBlock* const block)
10271059
10281060 JITDUMP (" -- prev tree is viable candidate for relop fwd sub!\n " );
10291061 candidateTree = prevTreeRHS;
1030- reverse = domIsRevRelop ;
1062+ candidateStmt = prevStmt ;
10311063 }
10321064
10331065 if (candidateTree == nullptr )
10341066 {
10351067 return false ;
10361068 }
10371069
1038- // Looks good -- we are going to forward-sub candidateTree
1039- //
1040- GenTree* const substituteTree = gtCloneExpr (candidateTree);
1070+ GenTree* substituteTree = nullptr ;
1071+ bool usedCopy = false ;
1072+
1073+ if (candidateStmt->GetNextStmt () == stmt)
1074+ {
1075+ // We are going forward-sub candidateTree
1076+ //
1077+ substituteTree = candidateTree;
1078+ }
1079+ else
1080+ {
1081+ // We going to forward-sub a copy of candidateTree
1082+ //
1083+ assert (!sideEffect);
1084+ substituteTree = gtCloneExpr (candidateTree);
1085+ usedCopy = true ;
1086+ }
10411087
1042- // If we need the reverse compare, make it so
1088+ // If we need the reverse compare, make it so.
1089+ // We also need to set a proper VN.
10431090 //
1044- if (reverse)
1091+ if ((vnRelationMatch == ValueNumStore::VN_RELATION_KIND::VRK_Reverse) ||
1092+ (vnRelationMatch == ValueNumStore::VN_RELATION_KIND::VRK_SwapReverse))
10451093 {
1094+ // Copy the vn info as it will be trashed when we change the oper.
1095+ //
1096+ ValueNumPair origVNP = substituteTree->gtVNPair ;
1097+
1098+ // Update the tree. Note we don't actually swap operands...?
1099+ //
10461100 substituteTree->SetOper (GenTree::ReverseRelop (substituteTree->OperGet ()));
10471101
1048- // This substitute tree will have the VN the same as the original.
1049- // (modulo excep sets...? hmmm)
1050- substituteTree->SetVNsFromNode (tree);
1102+ // Compute the right set of VNs for this new tree.
1103+ //
1104+ ValueNum origNormConVN = vnStore->VNConservativeNormalValue (origVNP);
1105+ ValueNum origNormLibVN = vnStore->VNLiberalNormalValue (origVNP);
1106+ ValueNum newNormConVN = vnStore->GetRelatedRelop (origNormConVN, ValueNumStore::VN_RELATION_KIND::VRK_Reverse);
1107+ ValueNum newNormLibVN = vnStore->GetRelatedRelop (origNormLibVN, ValueNumStore::VN_RELATION_KIND::VRK_Reverse);
1108+ ValueNumPair newNormalVNP (newNormLibVN, newNormConVN);
1109+ ValueNumPair origExcVNP = vnStore->VNPExceptionSet (origVNP);
1110+ ValueNumPair newVNP = vnStore->VNPWithExc (newNormalVNP, origExcVNP);
1111+
1112+ substituteTree->SetVNs (newVNP);
10511113 }
10521114
1053- // We just intentionally created a duplicate -- we don't want CSE to undo this.
1054- // (hopefully, the original is now dead).
1055- //
1056- // Also this relop is now a subtree of a jump.
1115+ // This relop is now a subtree of a jump.
10571116 //
1058- substituteTree->gtFlags |= (GTF_DONT_CSE | GTF_RELOP_JMP_USED );
1117+ substituteTree->gtFlags |= (GTF_RELOP_JMP_USED | GTF_DONT_CSE );
10591118
10601119 // Swap in the new tree.
10611120 //
@@ -1066,6 +1125,13 @@ bool Compiler::optRedundantRelop(BasicBlock* const block)
10661125
10671126 DEBUG_DESTROY_NODE (tree);
10681127
1128+ // If we didn't forward sub a copy, the candidateStmt must be removed.
1129+ //
1130+ if (!usedCopy)
1131+ {
1132+ fgRemoveStmt (block, candidateStmt);
1133+ }
1134+
10691135 JITDUMP (" -- done! new jump tree is\n " );
10701136 DISPTREE (jumpTree);
10711137
0 commit comments