@@ -2669,6 +2669,80 @@ foldRoundUpIntegerWithPow2Alignment(SelectInst &SI,
2669
2669
return R;
2670
2670
}
2671
2671
2672
+ // / Look for patterns like
2673
+ // / %outer.cond = select i1 %inner.cond, i1 %alt.cond, i1 false
2674
+ // / %inner.sel = select i1 %inner.cond, i8 %inner.sel.t, i8 %inner.sel.f
2675
+ // / %outer.sel = select i1 %outer.cond, i8 %outer.sel.t, i8 %inner.sel
2676
+ // / and rewrite it as
2677
+ // / %inner.sel = select i1 %cond.alternative, i8 %sel.outer.t, i8 %sel.inner.t
2678
+ // / %sel.outer = select i1 %cond.inner, i8 %inner.sel, i8 %sel.inner.f
2679
+ static Instruction *foldNestedSelects (SelectInst &OuterSel,
2680
+ InstCombiner::BuilderTy &Builder) {
2681
+ // We must start with a `select`.
2682
+ Value *OuterCond, *InnerSel, *OuterSelFalseVal;
2683
+ match (&OuterSel, m_Select (m_Value (OuterCond), m_Value (InnerSel),
2684
+ m_Value (OuterSelFalseVal)));
2685
+
2686
+ // Canonicalize inversion of the outermost `select`'s condition.
2687
+ if (match (OuterCond, m_Not (m_Value (OuterCond))))
2688
+ std::swap (InnerSel, OuterSelFalseVal);
2689
+
2690
+ auto m_c_LogicalOp = [](auto L, auto R) {
2691
+ return m_CombineOr (m_c_LogicalAnd (L, R), m_c_LogicalOr (L, R));
2692
+ };
2693
+
2694
+ // The condition of the outermost select must be an `and`/`or`.
2695
+ if (!match (OuterCond, m_c_LogicalOp (m_Value (), m_Value ())))
2696
+ return nullptr ;
2697
+
2698
+ // To simplify logic, prefer the pattern variant with an `or`.
2699
+ bool IsAndVariant = match (OuterCond, m_LogicalAnd ());
2700
+ if (match (OuterCond, m_LogicalAnd ()))
2701
+ std::swap (InnerSel, OuterSelFalseVal);
2702
+
2703
+ // Profitability check - avoid increasing instruction count.
2704
+ if (none_of (ArrayRef<Value *>({OuterCond, InnerSel}),
2705
+ [](Value *V) { return V->hasOneUse (); }))
2706
+ return nullptr ;
2707
+
2708
+ // The appropriate hand of the outermost `select` must be a select itself.
2709
+ Value *InnerCond, *InnerSelTrueVal, *InnerSelFalseVal;
2710
+ if (!match (InnerSel, m_Select (m_Value (InnerCond), m_Value (InnerSelTrueVal),
2711
+ m_Value (InnerSelFalseVal))))
2712
+ return nullptr ;
2713
+
2714
+ // Canonicalize inversion of the innermost `select`'s condition.
2715
+ if (match (InnerCond, m_Not (m_Value (InnerCond))))
2716
+ std::swap (InnerSelTrueVal, InnerSelFalseVal);
2717
+
2718
+ Value *AltCond = nullptr ;
2719
+ auto matchOuterCond = [OuterCond, m_c_LogicalOp, &AltCond](auto m_InnerCond) {
2720
+ return match (OuterCond, m_c_LogicalOp (m_InnerCond, m_Value (AltCond)));
2721
+ };
2722
+
2723
+ // Finally, match the condition that was driving the outermost `select`,
2724
+ // it should be a logical operation between the condition that was driving
2725
+ // the innermost `select` (after accounting for the possible inversions
2726
+ // of the condition), and some other condition.
2727
+ if (matchOuterCond (m_Specific (InnerCond))) {
2728
+ // Done!
2729
+ } else if (Value * NotInnerCond; matchOuterCond (m_CombineAnd (
2730
+ m_Not (m_Specific (InnerCond)), m_Value (NotInnerCond)))) {
2731
+ // Done!
2732
+ std::swap (InnerSelTrueVal, InnerSelFalseVal);
2733
+ InnerCond = NotInnerCond;
2734
+ } else // Not the pattern we were looking for.
2735
+ return nullptr ;
2736
+
2737
+ Value *SelInner = Builder.CreateSelect (
2738
+ AltCond, IsAndVariant ? OuterSelFalseVal : InnerSelFalseVal,
2739
+ IsAndVariant ? InnerSelTrueVal : OuterSelFalseVal);
2740
+ SelInner->takeName (InnerSel);
2741
+ return SelectInst::Create (InnerCond,
2742
+ IsAndVariant ? SelInner : InnerSelTrueVal,
2743
+ IsAndVariant ? InnerSelFalseVal : SelInner);
2744
+ }
2745
+
2672
2746
Instruction *InstCombinerImpl::foldSelectOfBools (SelectInst &SI) {
2673
2747
Value *CondVal = SI.getCondition ();
2674
2748
Value *TrueVal = SI.getTrueValue ();
@@ -3292,5 +3366,8 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
3292
3366
}
3293
3367
}
3294
3368
3369
+ if (Instruction *I = foldNestedSelects (SI, Builder))
3370
+ return I;
3371
+
3295
3372
return nullptr ;
3296
3373
}
0 commit comments