@@ -115,14 +115,76 @@ struct RelopImplicationInfo
115115 ValueNumStore::VN_RELATION_KIND vnRelation = ValueNumStore::VN_RELATION_KIND::VRK_Same;
116116 // Can we draw an inference?
117117 bool canInfer = false ;
118- // If canInfer and ominating relop is true, can we infer value of dominated relop?
118+ // If canInfer and dominating relop is true, can we infer value of dominated relop?
119119 bool canInferFromTrue = true ;
120120 // If canInfer and dominating relop is false, can we infer value of dominated relop?
121121 bool canInferFromFalse = true ;
122122 // Reverse the sense of the inference
123123 bool reverseSense = false ;
124124};
125125
126+ // ------------------------------------------------------------------------
127+ // RelopImplicationRule
128+ //
129+ // A rule allowing inference between two otherwise unrelated relops.
130+ // Related relops are handled via s_vnRelations above.
131+ //
132+ struct RelopImplicationRule
133+ {
134+ VNFunc domRelop;
135+ bool canInferFromTrue;
136+ bool canInferFromFalse;
137+ VNFunc treeRelop;
138+ bool reverse;
139+ };
140+
141+ // ------------------------------------------------------------------------
142+ // s_implicationRules: rule table for unrelated relops
143+ //
144+ // clang-format off
145+ //
146+ #define V (x ) (VNFunc)GT_##x
147+
148+ static const RelopImplicationRule s_implicationRules[] =
149+ {
150+ // EQ
151+ {V (EQ), true , false , V (GE), false },
152+ {V (EQ), true , false , V (LE), false },
153+ {V (EQ), true , false , V (GT), true },
154+ {V (EQ), true , false , V (LT), true },
155+
156+ // NE
157+ {V (NE), false , true , V (GE), true },
158+ {V (NE), false , true , V (LE), true },
159+ {V (NE), false , true , V (GT), false },
160+ {V (NE), false , true , V (LT), false },
161+
162+ // LE
163+ {V (LE), false , true , V (EQ), false },
164+ {V (LE), false , true , V (NE), true },
165+ {V (LE), false , true , V (GE), true },
166+ {V (LE), false , true , V (LT), false },
167+
168+ // GT
169+ {V (GT), true , false , V (EQ), true },
170+ {V (GT), true , false , V (NE), false },
171+ {V (GT), true , false , V (GE), false },
172+ {V (GT), true , false , V (LT), true },
173+
174+ // GE
175+ {V (GE), false , true , V (EQ), false },
176+ {V (GE), false , true , V (NE), true },
177+ {V (GE), false , true , V (LE), true },
178+ {V (GE), false , true , V (GT), false },
179+
180+ // LT
181+ {V (LT), true , false , V (EQ), true },
182+ {V (LT), true , false , V (NE), false },
183+ {V (LT), true , false , V (LE), false },
184+ {V (LT), true , false , V (GT), true },
185+ };
186+ // clang-format on
187+
126188// ------------------------------------------------------------------------
127189// optRedundantBranch: try and optimize a possibly redundant branch
128190//
@@ -146,9 +208,14 @@ struct RelopImplicationInfo
146208// a false taken branch, so we need to invert the sense of our
147209// inferences.
148210//
149- // Note we could also infer the tree relop's value from other
211+ // We can also partially infer the tree relop's value from other
150212// dominating relops, for example, (x >= 0) dominating (x > 0).
151- // That is left as a future enhancement.
213+ //
214+ // We don't get all the cases here we could. Still to do:
215+ // * two unsigned compares, same operands
216+ // * mixture of signed/unsigned compares, same operands
217+ // * mixture of compares, one operand same, other operands different constants
218+ // x > 1 ==> x >= 0
152219//
153220void Compiler::optRelopImpliesRelop (RelopImplicationInfo* rii)
154221{
@@ -178,15 +245,72 @@ void Compiler::optRelopImpliesRelop(RelopImplicationInfo* rii)
178245
179246 genTreeOps const oper = genTreeOps (funcApp.m_func );
180247
248+ // Exclude floating point relops.
249+ //
250+ if (varTypeIsFloating (vnStore->TypeOfVN (funcApp.m_args [0 ])))
251+ {
252+ return ;
253+ }
254+
255+ #ifdef DEBUG
256+ static ConfigMethodRange JitEnableRboRange;
257+ JitEnableRboRange.EnsureInit (JitConfig.JitEnableRboRange ());
258+ const unsigned hash = impInlineRoot ()->info .compMethodHash ();
259+ const bool inRange = JitEnableRboRange.Contains (hash);
260+ #else
261+ const bool inRange = true ;
262+ #endif
263+
264+ // Dominating compare has the form R(x,y)
265+ // See if tree compare has the form R*(x,y) or R*(y,x) where we can infer R* from R
266+ //
267+ // Could also extend to the unsigned VN relops.
268+ //
269+ VNFuncApp treeApp;
270+ if (inRange && GenTree::OperIsCompare (oper) && vnStore->GetVNFunc (rii->treeNormVN , &treeApp))
271+ {
272+ genTreeOps const treeOper = genTreeOps (treeApp.m_func );
273+ genTreeOps domOper = oper;
274+
275+ if (((treeApp.m_args [0 ] == funcApp.m_args [0 ]) && (treeApp.m_args [1 ] == funcApp.m_args [1 ])) ||
276+ ((treeApp.m_args [0 ] == funcApp.m_args [1 ]) && (treeApp.m_args [1 ] == funcApp.m_args [0 ])))
277+ {
278+ const bool swapped = (treeApp.m_args [0 ] == funcApp.m_args [1 ]);
279+
280+ if (swapped)
281+ {
282+ domOper = GenTree::SwapRelop (domOper);
283+ }
284+
285+ for (const RelopImplicationRule& rule : s_implicationRules)
286+ {
287+ if ((rule.domRelop == (VNFunc)domOper) && (rule.treeRelop == (VNFunc)treeOper))
288+ {
289+ rii->canInfer = true ;
290+ rii->vnRelation = ValueNumStore::VN_RELATION_KIND::VRK_Inferred;
291+ rii->canInferFromTrue = rule.canInferFromTrue ;
292+ rii->canInferFromFalse = rule.canInferFromFalse ;
293+ rii->reverseSense = rule.reverse ;
294+
295+ JITDUMP (" Can infer %s from [%s] %s\n " , GenTree::OpName (treeOper),
296+ rii->canInferFromTrue ? " true" : " false" , GenTree::OpName (oper));
297+ return ;
298+ }
299+ }
300+ }
301+ }
302+
303+ // See if dominating compare is a compound comparison that might
304+ // tell us the value of the tree compare.
305+ //
181306 // Look for {EQ,NE}({AND,OR,NOT}, 0)
182307 //
183308 if (!GenTree::StaticOperIs (oper, GT_EQ, GT_NE))
184309 {
185310 return ;
186311 }
187312
188- const ValueNum constantVN = funcApp.m_args [1 ];
189- if (constantVN != vnStore->VNZeroForType (TYP_INT))
313+ if (funcApp.m_args [1 ] != vnStore->VNZeroForType (TYP_INT))
190314 {
191315 return ;
192316 }
@@ -309,11 +433,14 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
309433 //
310434 if (vnStore->IsVNConstant (treeNormVN))
311435 {
312-
313436 relopValue = (treeNormVN == vnStore->VNZeroForType (TYP_INT)) ? 0 : 1 ;
314437 JITDUMP (" Relop [%06u] " FMT_BB " has known value %s\n " , dspTreeID (tree), block->bbNum ,
315438 relopValue == 0 ? " false" : " true" );
316439 }
440+ else
441+ {
442+ JITDUMP (" Relop [%06u] " FMT_BB " value unknown, trying inference\n " , dspTreeID (tree), block->bbNum );
443+ }
317444
318445 bool trySpeculativeDom = false ;
319446 while ((relopValue == -1 ) && !trySpeculativeDom)
@@ -370,11 +497,26 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
370497 return false ;
371498 }
372499
500+ // Was this an inference from an unrelated relop (GE => GT, say)?
501+ //
502+ const bool domIsInferredRelop = (rii.vnRelation == ValueNumStore::VN_RELATION_KIND::VRK_Inferred);
503+
373504 // The compare in "tree" is redundant.
374505 // Is there a unique path from the dominating compare?
375506 //
376- JITDUMP (" \n Dominator " FMT_BB " of " FMT_BB " has relop with %s liberal VN\n " , domBlock->bbNum ,
377- block->bbNum , ValueNumStore::VNRelationString (rii.vnRelation ));
507+ if (domIsInferredRelop)
508+ {
509+ // This inference should be one-sided
510+ //
511+ assert (rii.canInferFromTrue ^ rii.canInferFromFalse );
512+ JITDUMP (" \n Dominator " FMT_BB " of " FMT_BB " has same VN operands but different relop\n " ,
513+ domBlock->bbNum , block->bbNum );
514+ }
515+ else
516+ {
517+ JITDUMP (" \n Dominator " FMT_BB " of " FMT_BB " has relop with %s liberal VN\n " , domBlock->bbNum ,
518+ block->bbNum , ValueNumStore::VNRelationString (rii.vnRelation ));
519+ }
378520 DISPTREE (domCmpTree);
379521 JITDUMP (" Redundant compare; current relop:\n " );
380522 DISPTREE (tree);
@@ -415,7 +557,7 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
415557 {
416558 // Taken jump in dominator reaches, fall through doesn't; relop must be true/false.
417559 //
418- const bool relopIsTrue = rii.reverseSense ^ domIsSameRelop;
560+ const bool relopIsTrue = rii.reverseSense ^ ( domIsSameRelop | domIsInferredRelop) ;
419561 JITDUMP (" Jump successor " FMT_BB " of " FMT_BB " reaches, relop [%06u] must be %s\n " ,
420562 domBlock->bbJumpDest ->bbNum , domBlock->bbNum , dspTreeID (tree),
421563 relopIsTrue ? " true" : " false" );
@@ -426,14 +568,14 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
426568 {
427569 // Fall through from dominator reaches, taken jump doesn't; relop must be false/true.
428570 //
429- const bool relopIsFalse = rii.reverseSense ^ domIsSameRelop;
571+ const bool relopIsFalse = rii.reverseSense ^ ( domIsSameRelop | domIsInferredRelop) ;
430572 JITDUMP (" Fall through successor " FMT_BB " of " FMT_BB " reaches, relop [%06u] must be %s\n " ,
431573 domBlock->bbNext ->bbNum , domBlock->bbNum , dspTreeID (tree),
432574 relopIsFalse ? " false" : " true" );
433575 relopValue = relopIsFalse ? 0 : 1 ;
434576 break ;
435577 }
436- else
578+ else if (!falseReaches && !trueReaches)
437579 {
438580 // No apparent path from the dominating BB.
439581 //
@@ -446,8 +588,15 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
446588 //
447589 // No point in looking further up the tree.
448590 //
591+ JITDUMP (" inference failed -- no apparent path, will stop looking\n " );
449592 break ;
450593 }
594+ else
595+ {
596+ // Keep looking up the dom tree
597+ //
598+ JITDUMP (" inference failed -- will keep looking higher\n " );
599+ }
451600 }
452601 }
453602 }
0 commit comments