[llvm] [InstCombine] Missing optimization: fold mul (select a, b), (select b, a) to mul a, b (PR #74953)

Sizov Nikita via llvm-commits llvm-commits at lists.llvm.org
Sun Dec 10 14:31:43 PST 2023


https://github.com/snikitav updated https://github.com/llvm/llvm-project/pull/74953

>From 0df07c95613a884480f26d4cbed7f9e7492e04ae Mon Sep 17 00:00:00 2001
From: Sizov Nikita <s.nikita.v at gmail.com>
Date: Sun, 10 Dec 2023 01:25:36 +0300
Subject: [PATCH] Implement symmetrical selects feeding commutative
 BioOp/Intrinsic folding

---
 .../InstCombine/InstCombineCalls.cpp          |   23 +
 .../InstCombine/InstCombineInternal.h         |    1 +
 .../InstCombine/InstructionCombining.cpp      |   11 +
 .../commutative-operation-over-selects.ll     | 1117 +++++++++++++++++
 4 files changed, 1152 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/commutative-operation-over-selects.ll

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 255ce6973a16fb..7e475e437b25b9 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -1536,6 +1536,9 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
   }
 
   if (II->isCommutative()) {
+    if (Instruction *I = foldCommutativeIntrinsicOverSelects(*II))
+      return I;
+
     if (CallInst *NewCall = canonicalizeConstantArg0ToArg1(CI))
       return NewCall;
   }
@@ -4217,3 +4220,23 @@ InstCombinerImpl::transformCallThroughTrampoline(CallBase &Call,
   Call.setCalledFunction(FTy, NestF);
   return &Call;
 }
+
+// op(select(%v, %x, %y), select(%v, %y, %x)) --> op(%x, %y)
+Instruction *
+InstCombinerImpl::foldCommutativeIntrinsicOverSelects(IntrinsicInst &II) {
+  assert(II.isCommutative());
+
+  Value *A, *B, *C;
+  bool LHSIsSelect =
+      match(II.getOperand(0), m_Select(m_Value(A), m_Value(B), m_Value(C)));
+  bool RHSIsSymmetricalSelect =
+      match(II.getOperand(1), m_Select(m_Specific(A), m_Specific(C), m_Specific(B)));
+
+  if (LHSIsSelect && RHSIsSymmetricalSelect) {
+    replaceOperand(II, 0, B);
+    replaceOperand(II, 1, C);
+    return &II;
+  }
+
+  return nullptr;
+}
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index bb620ad8d41c13..1d50fa9b6bf74b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -276,6 +276,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
   bool transformConstExprCastCall(CallBase &Call);
   Instruction *transformCallThroughTrampoline(CallBase &Call,
                                               IntrinsicInst &Tramp);
+  Instruction *foldCommutativeIntrinsicOverSelects(IntrinsicInst &II);
 
   Value *simplifyMaskedLoad(IntrinsicInst &II);
   Instruction *simplifyMaskedStore(IntrinsicInst &II);
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index f072f5cec3094a..16924a3348e94e 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -1132,6 +1132,17 @@ Value *InstCombinerImpl::SimplifySelectsFeedingBinaryOp(BinaryOperator &I,
   };
 
   if (LHSIsSelect && RHSIsSelect && A == D) {
+    // op(select(%v, %x, %y), select(%v, %y, %x)) --> op(%x, %y)
+    if (I.isCommutative() && B == F && C == E) {
+      Value *BI = Builder.CreateBinOp(I.getOpcode(), B, E);
+      if (auto *BO = dyn_cast<BinaryOperator>(BI)) {
+        BO->copyIRFlags(BI);
+        BO->dropPoisonGeneratingFlags();
+      }
+      BI->takeName(&I);
+      return BI;
+    }
+
     // (A ? B : C) op (A ? E : F) -> A ? (B op E) : (C op F)
     Cond = A;
     True = simplifyBinOp(Opcode, B, E, FMF, Q);
diff --git a/llvm/test/Transforms/InstCombine/commutative-operation-over-selects.ll b/llvm/test/Transforms/InstCombine/commutative-operation-over-selects.ll
new file mode 100644
index 00000000000000..69b1f9f36426bb
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/commutative-operation-over-selects.ll
@@ -0,0 +1,1117 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -passes=instcombine -S < %s | FileCheck %s
+
+declare float @llvm.maxnum.f32(float %a, float %b)
+declare float @llvm.minnum.f32(float %a, float %b)
+declare float @llvm.maximum.f32(float %a, float %b)
+declare float @llvm.minimum.f32(float %a, float %b)
+declare i32 @llvm.smax.i32(i32 %a, i32 %b)
+declare i32 @llvm.smin.i32(i32 %a, i32 %b)
+declare i32 @llvm.umax.i32(i32 %a, i32 %b)
+declare i32 @llvm.umin.i32(i32 %a, i32 %b)
+declare i16 @llvm.sadd.sat.i16(i16 %a, i16 %b)
+declare i16 @llvm.uadd.sat.i16(i16 %a, i16 %b)
+declare {i16, i1} @llvm.sadd.with.overflow.i16(i16 %a, i16 %b)
+declare {i16, i1} @llvm.uadd.with.overflow.i16(i16 %a, i16 %b)
+declare {i16, i1} @llvm.smul.with.overflow.i16(i16 %a, i16 %b)
+declare {i16, i1} @llvm.umul.with.overflow.i16(i16 %a, i16 %b)
+declare i16 @llvm.smul.fix.i16(i16 %a, i16 %b, i32 %scale)
+declare i16 @llvm.umul.fix.i16(i16 %a, i16 %b, i32 %scale)
+declare i16 @llvm.smul.fix.sat.i16(i16 %a, i16 %b, i32 %scale)
+declare i16 @llvm.umul.fix.sat.i16(i16 %a, i16 %b, i32 %scale)
+declare float @llvm.fma.f32(float %a, float %b, float %c)
+declare float @llvm.fmuladd.f32(float %a, float %b, float %c)
+
+define i8 @fold_select_mul(i1 %c, i8 %a, i8 %b) {
+; CHECK-LABEL: define i8 @fold_select_mul(
+; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = mul i8 [[B]], [[A]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c, i8 %b, i8 %a
+  %ret = mul i8 %s1, %s0
+  ret i8 %ret
+}
+
+define i8 @fold_select_mul_nsw_nuw_preserve(i1 %c, i8 %a, i8 %b) {
+; CHECK-LABEL: define i8 @fold_select_mul_nsw_nuw_preserve(
+; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = mul i8 [[B]], [[A]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c, i8 %b, i8 %a
+  %ret = mul nsw nuw i8 %s1, %s0
+  ret i8 %ret
+}
+
+define <2 x i4> @fold_select_mul_vec2(i1 %c, <2 x i4> %a, <2 x i4> %b) {
+; CHECK-LABEL: define <2 x i4> @fold_select_mul_vec2(
+; CHECK-SAME: i1 [[C:%.*]], <2 x i4> [[A:%.*]], <2 x i4> [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = mul <2 x i4> [[B]], [[A]]
+; CHECK-NEXT:    ret <2 x i4> [[RET]]
+;
+  %s0 = select i1 %c, <2 x i4> %a, <2 x i4> %b
+  %s1 = select i1 %c, <2 x i4> %b, <2 x i4> %a
+  %ret = mul <2 x i4> %s1, %s0
+  ret <2 x i4> %ret
+}
+
+define i8 @fold_select_add(i1 %c, i8 %a, i8 %b) {
+; CHECK-LABEL: define i8 @fold_select_add(
+; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = add i8 [[B]], [[A]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c, i8 %b, i8 %a
+  %ret = add i8 %s1, %s0
+  ret i8 %ret
+}
+
+define <2 x i4> @fold_select_add_vec2(i1 %c, <2 x i4> %a, <2 x i4> %b) {
+; CHECK-LABEL: define <2 x i4> @fold_select_add_vec2(
+; CHECK-SAME: i1 [[C:%.*]], <2 x i4> [[A:%.*]], <2 x i4> [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = add <2 x i4> [[B]], [[A]]
+; CHECK-NEXT:    ret <2 x i4> [[RET]]
+;
+  %s0 = select i1 %c, <2 x i4> %a, <2 x i4> %b
+  %s1 = select i1 %c, <2 x i4> %b, <2 x i4> %a
+  %ret = add <2 x i4> %s1, %s0
+  ret <2 x i4> %ret
+}
+
+define i8 @fold_select_and(i1 %c, i8 %a, i8 %b) {
+; CHECK-LABEL: define i8 @fold_select_and(
+; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = and i8 [[B]], [[A]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c, i8 %b, i8 %a
+  %ret = and i8 %s1, %s0
+  ret i8 %ret
+}
+
+define <2 x i4> @fold_select_and_vec2(i1 %c, <2 x i4> %a, <2 x i4> %b) {
+; CHECK-LABEL: define <2 x i4> @fold_select_and_vec2(
+; CHECK-SAME: i1 [[C:%.*]], <2 x i4> [[A:%.*]], <2 x i4> [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = and <2 x i4> [[B]], [[A]]
+; CHECK-NEXT:    ret <2 x i4> [[RET]]
+;
+  %s0 = select i1 %c, <2 x i4> %a, <2 x i4> %b
+  %s1 = select i1 %c, <2 x i4> %b, <2 x i4> %a
+  %ret = and <2 x i4> %s1, %s0
+  ret <2 x i4> %ret
+}
+
+define i8 @fold_select_or(i1 %c, i8 %a, i8 %b) {
+; CHECK-LABEL: define i8 @fold_select_or(
+; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = or i8 [[B]], [[A]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c, i8 %b, i8 %a
+  %ret = or i8 %s1, %s0
+  ret i8 %ret
+}
+
+define <2 x i4> @fold_select_or_vec2(i1 %c, <2 x i4> %a, <2 x i4> %b) {
+; CHECK-LABEL: define <2 x i4> @fold_select_or_vec2(
+; CHECK-SAME: i1 [[C:%.*]], <2 x i4> [[A:%.*]], <2 x i4> [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = or <2 x i4> [[B]], [[A]]
+; CHECK-NEXT:    ret <2 x i4> [[RET]]
+;
+  %s0 = select i1 %c, <2 x i4> %a, <2 x i4> %b
+  %s1 = select i1 %c, <2 x i4> %b, <2 x i4> %a
+  %ret = or <2 x i4> %s1, %s0
+  ret <2 x i4> %ret
+}
+
+define i8 @fold_select_xor(i1 %c, i8 %a, i8 %b) {
+; CHECK-LABEL: define i8 @fold_select_xor(
+; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = xor i8 [[B]], [[A]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c, i8 %b, i8 %a
+  %ret = xor i8 %s1, %s0
+  ret i8 %ret
+}
+
+define <2 x i4> @fold_select_xor_vec2(i1 %c, <2 x i4> %a, <2 x i4> %b) {
+; CHECK-LABEL: define <2 x i4> @fold_select_xor_vec2(
+; CHECK-SAME: i1 [[C:%.*]], <2 x i4> [[A:%.*]], <2 x i4> [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = xor <2 x i4> [[B]], [[A]]
+; CHECK-NEXT:    ret <2 x i4> [[RET]]
+;
+  %s0 = select i1 %c, <2 x i4> %a, <2 x i4> %b
+  %s1 = select i1 %c, <2 x i4> %b, <2 x i4> %a
+  %ret = xor <2 x i4> %s1, %s0
+  ret <2 x i4> %ret
+}
+
+define float @fold_select_fadd(i1 %c, float %a, float %b) {
+; CHECK-LABEL: define float @fold_select_fadd(
+; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = fadd float [[B]], [[A]]
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c, float %b, float %a
+  %ret = fadd float %s1, %s0
+  ret float %ret
+}
+
+define <2 x float> @fold_select_fadd_vec2(i1 %c, <2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: define <2 x float> @fold_select_fadd_vec2(
+; CHECK-SAME: i1 [[C:%.*]], <2 x float> [[A:%.*]], <2 x float> [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = fadd <2 x float> [[B]], [[A]]
+; CHECK-NEXT:    ret <2 x float> [[RET]]
+;
+  %s0 = select i1 %c, <2 x float> %a, <2 x float> %b
+  %s1 = select i1 %c, <2 x float> %b, <2 x float> %a
+  %ret = fadd <2 x float> %s1, %s0
+  ret <2 x float> %ret
+}
+
+define float @fold_select_fmul(i1 %c, float %a, float %b) {
+; CHECK-LABEL: define float @fold_select_fmul(
+; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = fmul float [[B]], [[A]]
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c, float %b, float %a
+  %ret = fmul float %s1, %s0
+  ret float %ret
+}
+
+define <2 x float> @fold_select_fmul_vec2(i1 %c, <2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: define <2 x float> @fold_select_fmul_vec2(
+; CHECK-SAME: i1 [[C:%.*]], <2 x float> [[A:%.*]], <2 x float> [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = fmul <2 x float> [[B]], [[A]]
+; CHECK-NEXT:    ret <2 x float> [[RET]]
+;
+  %s0 = select i1 %c, <2 x float> %a, <2 x float> %b
+  %s1 = select i1 %c, <2 x float> %b, <2 x float> %a
+  %ret = fmul <2 x float> %s1, %s0
+  ret <2 x float> %ret
+}
+
+;
+
+define float @fold_select_maxnum(i1 %c, float %a, float %b) {
+; CHECK-LABEL: define float @fold_select_maxnum(
+; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.maxnum.f32(float [[B]], float [[A]])
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c, float %b, float %a
+  %ret = call float @llvm.maxnum.f32(float %s1, float %s0)
+  ret float %ret
+}
+
+define float @fold_select_minnum(i1 %c, float %a, float %b) {
+; CHECK-LABEL: define float @fold_select_minnum(
+; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.minnum.f32(float [[B]], float [[A]])
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c, float %b, float %a
+  %ret = call float @llvm.minnum.f32(float %s1, float %s0)
+  ret float %ret
+}
+
+define float @fold_select_maximum(i1 %c, float %a, float %b) {
+; CHECK-LABEL: define float @fold_select_maximum(
+; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.maximum.f32(float [[B]], float [[A]])
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c, float %b, float %a
+  %ret = call float @llvm.maximum.f32(float %s1, float %s0)
+  ret float %ret
+}
+
+define float @fold_select_minimum(i1 %c, float %a, float %b) {
+; CHECK-LABEL: define float @fold_select_minimum(
+; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.minimum.f32(float [[B]], float [[A]])
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c, float %b, float %a
+  %ret = call float @llvm.minimum.f32(float %s1, float %s0)
+  ret float %ret
+}
+
+define i32 @fold_select_smax(i1 %c, i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @fold_select_smax(
+; CHECK-SAME: i1 [[C:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call i32 @llvm.smax.i32(i32 [[B]], i32 [[A]])
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %s0 = select i1 %c, i32 %a, i32 %b
+  %s1 = select i1 %c, i32 %b, i32 %a
+  %ret = call i32 @llvm.smax.i32(i32 %s1, i32 %s0)
+  ret i32 %ret
+}
+
+define i32 @fold_select_smin(i1 %c, i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @fold_select_smin(
+; CHECK-SAME: i1 [[C:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call i32 @llvm.smin.i32(i32 [[B]], i32 [[A]])
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %s0 = select i1 %c, i32 %a, i32 %b
+  %s1 = select i1 %c, i32 %b, i32 %a
+  %ret = call i32 @llvm.smin.i32(i32 %s1, i32 %s0)
+  ret i32 %ret
+}
+
+define i32 @fold_select_umax(i1 %c, i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @fold_select_umax(
+; CHECK-SAME: i1 [[C:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call i32 @llvm.umax.i32(i32 [[B]], i32 [[A]])
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %s0 = select i1 %c, i32 %a, i32 %b
+  %s1 = select i1 %c, i32 %b, i32 %a
+  %ret = call i32 @llvm.umax.i32(i32 %s1, i32 %s0)
+  ret i32 %ret
+}
+
+define i32 @fold_select_umin(i1 %c, i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @fold_select_umin(
+; CHECK-SAME: i1 [[C:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call i32 @llvm.umin.i32(i32 [[B]], i32 [[A]])
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %s0 = select i1 %c, i32 %a, i32 %b
+  %s1 = select i1 %c, i32 %b, i32 %a
+  %ret = call i32 @llvm.umin.i32(i32 %s1, i32 %s0)
+  ret i32 %ret
+}
+
+define i16 @fold_select_sadd_sat(i1 %c, i16 %a, i16 %b) {
+; CHECK-LABEL: define i16 @fold_select_sadd_sat(
+; CHECK-SAME: i1 [[C:%.*]], i16 [[A:%.*]], i16 [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call i16 @llvm.sadd.sat.i16(i16 [[B]], i16 [[A]])
+; CHECK-NEXT:    ret i16 [[RET]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c, i16 %b, i16 %a
+  %ret = call i16 @llvm.sadd.sat.i16(i16 %s1, i16 %s0)
+  ret i16 %ret
+}
+
+define i16 @fold_select_uadd_sat(i1 %c, i16 %a, i16 %b) {
+; CHECK-LABEL: define i16 @fold_select_uadd_sat(
+; CHECK-SAME: i1 [[C:%.*]], i16 [[A:%.*]], i16 [[B:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call i16 @llvm.uadd.sat.i16(i16 [[B]], i16 [[A]])
+; CHECK-NEXT:    ret i16 [[RET]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c, i16 %b, i16 %a
+  %ret = call i16 @llvm.uadd.sat.i16(i16 %s1, i16 %s0)
+  ret i16 %ret
+}
+
+define i16 @fold_select_sadd_with_overflow(i1 %c, i16 %a, i16 %b) {
+; CHECK-LABEL: define i16 @fold_select_sadd_with_overflow(
+; CHECK-SAME: i1 [[C:%.*]], i16 [[A:%.*]], i16 [[B:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = call { i16, i1 } @llvm.sadd.with.overflow.i16(i16 [[B]], i16 [[A]])
+; CHECK-NEXT:    [[OBIT:%.*]] = extractvalue { i16, i1 } [[RES]], 1
+; CHECK-NEXT:    br i1 [[OBIT]], label [[OVERFLOW:%.*]], label [[NORMAL:%.*]]
+; CHECK:       overflow:
+; CHECK-NEXT:    ret i16 0
+; CHECK:       normal:
+; CHECK-NEXT:    [[SUM:%.*]] = extractvalue { i16, i1 } [[RES]], 0
+; CHECK-NEXT:    ret i16 [[SUM]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c, i16 %b, i16 %a
+  %res = call {i16, i1} @llvm.sadd.with.overflow.i16(i16 %s1, i16 %s0)
+  %obit = extractvalue {i16, i1} %res, 1
+  br i1 %obit, label %overflow, label %normal
+overflow:
+  ret i16 0
+normal:
+  %sum = extractvalue {i16, i1} %res, 0
+  ret i16 %sum
+}
+
+define i16 @fold_select_uadd_with_overflow(i1 %c, i16 %a, i16 %b) {
+; CHECK-LABEL: define i16 @fold_select_uadd_with_overflow(
+; CHECK-SAME: i1 [[C:%.*]], i16 [[A:%.*]], i16 [[B:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = call { i16, i1 } @llvm.uadd.with.overflow.i16(i16 [[B]], i16 [[A]])
+; CHECK-NEXT:    [[OBIT:%.*]] = extractvalue { i16, i1 } [[RES]], 1
+; CHECK-NEXT:    br i1 [[OBIT]], label [[OVERFLOW:%.*]], label [[NORMAL:%.*]]
+; CHECK:       overflow:
+; CHECK-NEXT:    ret i16 0
+; CHECK:       normal:
+; CHECK-NEXT:    [[SUM:%.*]] = extractvalue { i16, i1 } [[RES]], 0
+; CHECK-NEXT:    ret i16 [[SUM]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c, i16 %b, i16 %a
+  %res = call {i16, i1} @llvm.uadd.with.overflow.i16(i16 %s1, i16 %s0)
+  %obit = extractvalue {i16, i1} %res, 1
+  br i1 %obit, label %overflow, label %normal
+overflow:
+  ret i16 0
+normal:
+  %sum = extractvalue {i16, i1} %res, 0
+  ret i16 %sum
+}
+
+define i16 @fold_select_smul_with_overflow(i1 %c, i16 %a, i16 %b) {
+; CHECK-LABEL: define i16 @fold_select_smul_with_overflow(
+; CHECK-SAME: i1 [[C:%.*]], i16 [[A:%.*]], i16 [[B:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = call { i16, i1 } @llvm.smul.with.overflow.i16(i16 [[B]], i16 [[A]])
+; CHECK-NEXT:    [[OBIT:%.*]] = extractvalue { i16, i1 } [[RES]], 1
+; CHECK-NEXT:    br i1 [[OBIT]], label [[OVERFLOW:%.*]], label [[NORMAL:%.*]]
+; CHECK:       overflow:
+; CHECK-NEXT:    ret i16 0
+; CHECK:       normal:
+; CHECK-NEXT:    [[MUL:%.*]] = extractvalue { i16, i1 } [[RES]], 0
+; CHECK-NEXT:    ret i16 [[MUL]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c, i16 %b, i16 %a
+  %res = call {i16, i1} @llvm.smul.with.overflow.i16(i16 %s1, i16 %s0)
+  %obit = extractvalue {i16, i1} %res, 1
+  br i1 %obit, label %overflow, label %normal
+overflow:
+  ret i16 0
+normal:
+  %mul = extractvalue {i16, i1} %res, 0
+  ret i16 %mul
+}
+
+define i16 @fold_select_umul_with_overflow(i1 %c, i16 %a, i16 %b) {
+; CHECK-LABEL: define i16 @fold_select_umul_with_overflow(
+; CHECK-SAME: i1 [[C:%.*]], i16 [[A:%.*]], i16 [[B:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = call { i16, i1 } @llvm.umul.with.overflow.i16(i16 [[B]], i16 [[A]])
+; CHECK-NEXT:    [[OBIT:%.*]] = extractvalue { i16, i1 } [[RES]], 1
+; CHECK-NEXT:    br i1 [[OBIT]], label [[OVERFLOW:%.*]], label [[NORMAL:%.*]]
+; CHECK:       overflow:
+; CHECK-NEXT:    ret i16 0
+; CHECK:       normal:
+; CHECK-NEXT:    [[MUL:%.*]] = extractvalue { i16, i1 } [[RES]], 0
+; CHECK-NEXT:    ret i16 [[MUL]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c, i16 %b, i16 %a
+  %res = call {i16, i1} @llvm.umul.with.overflow.i16(i16 %s1, i16 %s0)
+  %obit = extractvalue {i16, i1} %res, 1
+  br i1 %obit, label %overflow, label %normal
+overflow:
+  ret i16 0
+normal:
+  %mul = extractvalue {i16, i1} %res, 0
+  ret i16 %mul
+}
+
+define i16 @fold_select_smul_fix(i1 %c, i16 %a, i16 %b, i32 %y) {
+; CHECK-LABEL: define i16 @fold_select_smul_fix(
+; CHECK-SAME: i1 [[C:%.*]], i16 [[A:%.*]], i16 [[B:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call i16 @llvm.smul.fix.i16(i16 [[B]], i16 [[A]], i32 1)
+; CHECK-NEXT:    ret i16 [[RET]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c, i16 %b, i16 %a
+  %ret = call i16 @llvm.smul.fix.i16(i16 %s1, i16 %s0, i32 1)
+  ret i16 %ret
+}
+
+define i16 @fold_select_umul_fix(i1 %c, i16 %a, i16 %b, i32 %y) {
+; CHECK-LABEL: define i16 @fold_select_umul_fix(
+; CHECK-SAME: i1 [[C:%.*]], i16 [[A:%.*]], i16 [[B:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call i16 @llvm.umul.fix.i16(i16 [[B]], i16 [[A]], i32 1)
+; CHECK-NEXT:    ret i16 [[RET]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c, i16 %b, i16 %a
+  %ret = call i16 @llvm.umul.fix.i16(i16 %s1, i16 %s0, i32 1)
+  ret i16 %ret
+}
+
+define i16 @fold_select_smul_fix_sat(i1 %c, i16 %a, i16 %b, i32 %y) {
+; CHECK-LABEL: define i16 @fold_select_smul_fix_sat(
+; CHECK-SAME: i1 [[C:%.*]], i16 [[A:%.*]], i16 [[B:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call i16 @llvm.smul.fix.sat.i16(i16 [[B]], i16 [[A]], i32 1)
+; CHECK-NEXT:    ret i16 [[RET]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c, i16 %b, i16 %a
+  %ret = call i16 @llvm.smul.fix.sat.i16(i16 %s1, i16 %s0, i32 1)
+  ret i16 %ret
+}
+
+define i16 @fold_select_umul_fix_sat(i1 %c, i16 %a, i16 %b, i32 %y) {
+; CHECK-LABEL: define i16 @fold_select_umul_fix_sat(
+; CHECK-SAME: i1 [[C:%.*]], i16 [[A:%.*]], i16 [[B:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call i16 @llvm.umul.fix.sat.i16(i16 [[B]], i16 [[A]], i32 1)
+; CHECK-NEXT:    ret i16 [[RET]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c, i16 %b, i16 %a
+  %ret = call i16 @llvm.umul.fix.sat.i16(i16 %s1, i16 %s0, i32 1)
+  ret i16 %ret
+}
+
+define float @fold_select_fma(i1 %c, float %a, float %b, float %y) {
+; CHECK-LABEL: define float @fold_select_fma(
+; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]], float [[Y:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.fma.f32(float [[B]], float [[A]], float [[Y]])
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c, float %b, float %a
+  %ret = call float @llvm.fma.f32(float %s1, float %s0, float %y)
+  ret float %ret
+}
+
+define float @fold_select_fmuladd(i1 %c, float %a, float %b, float %y) {
+; CHECK-LABEL: define float @fold_select_fmuladd(
+; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]], float [[Y:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.fmuladd.f32(float [[B]], float [[A]], float [[Y]])
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c, float %b, float %a
+  %ret = call float @llvm.fmuladd.f32(float %s1, float %s0, float %y)
+  ret float %ret
+}
+
+;negative tests:
+
+define i8 @fold_select_unmatch_mul_neg(i1 %c, i1 %c1, i8 %a, i8 %b) {
+; CHECK-LABEL: define i8 @fold_select_unmatch_mul_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i8 [[A]], i8 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i8 [[B]], i8 [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = mul i8 [[S1]], [[S0]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c1, i8 %b, i8 %a
+  %ret = mul i8 %s1, %s0
+  ret i8 %ret
+}
+
+define i8 @fold_select_mul_neg(i1 %c, i8 %a, i8 %b, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @fold_select_mul_neg(
+; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i8 [[A]], i8 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C]], i8 [[Y]], i8 [[Z]]
+; CHECK-NEXT:    [[RET:%.*]] = mul i8 [[S1]], [[S0]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c, i8 %y, i8 %z
+  %ret = mul i8 %s1, %s0
+  ret i8 %ret
+}
+
+define <2 x i4> @fold_select_mul_vec2_neg(i1 %c, <2 x i4> %a, <2 x i4> %b, <2 x i4> %y, <2 x i4> %z) {
+; CHECK-LABEL: define <2 x i4> @fold_select_mul_vec2_neg(
+; CHECK-SAME: i1 [[C:%.*]], <2 x i4> [[A:%.*]], <2 x i4> [[B:%.*]], <2 x i4> [[Y:%.*]], <2 x i4> [[Z:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], <2 x i4> [[A]], <2 x i4> [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C]], <2 x i4> [[Y]], <2 x i4> [[Z]]
+; CHECK-NEXT:    [[RET:%.*]] = mul <2 x i4> [[S1]], [[S0]]
+; CHECK-NEXT:    ret <2 x i4> [[RET]]
+;
+  %s0 = select i1 %c, <2 x i4> %a, <2 x i4> %b
+  %s1 = select i1 %c, <2 x i4> %y, <2 x i4> %z
+  %ret = mul <2 x i4> %s1, %s0
+  ret <2 x i4> %ret
+}
+
+define i8 @fold_select_unmatch_add_neg(i1 %c, i1 %c1, i8 %a, i8 %b) {
+; CHECK-LABEL: define i8 @fold_select_unmatch_add_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i8 [[A]], i8 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i8 [[B]], i8 [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = add i8 [[S1]], [[S0]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c1, i8 %b, i8 %a
+  %ret = add i8 %s1, %s0
+  ret i8 %ret
+}
+
+define i8 @fold_select_add_neg(i1 %c, i8 %a, i8 %b, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @fold_select_add_neg(
+; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i8 [[A]], i8 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C]], i8 [[Y]], i8 [[Z]]
+; CHECK-NEXT:    [[RET:%.*]] = add i8 [[S1]], [[S0]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c, i8 %y, i8 %z
+  %ret = add i8 %s1, %s0
+  ret i8 %ret
+}
+
+define <2 x i4> @fold_select_add_vec2_neg(i1 %c, <2 x i4> %a, <2 x i4> %b, <2 x i4> %y, <2 x i4> %z) {
+; CHECK-LABEL: define <2 x i4> @fold_select_add_vec2_neg(
+; CHECK-SAME: i1 [[C:%.*]], <2 x i4> [[A:%.*]], <2 x i4> [[B:%.*]], <2 x i4> [[Y:%.*]], <2 x i4> [[Z:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], <2 x i4> [[A]], <2 x i4> [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C]], <2 x i4> [[Y]], <2 x i4> [[Z]]
+; CHECK-NEXT:    [[RET:%.*]] = add <2 x i4> [[S1]], [[S0]]
+; CHECK-NEXT:    ret <2 x i4> [[RET]]
+;
+  %s0 = select i1 %c, <2 x i4> %a, <2 x i4> %b
+  %s1 = select i1 %c, <2 x i4> %y, <2 x i4> %z
+  %ret = add <2 x i4> %s1, %s0
+  ret <2 x i4> %ret
+}
+
+define i8 @fold_select_unmatch_and_neg(i1 %c, i1 %c1, i8 %a, i8 %b) {
+; CHECK-LABEL: define i8 @fold_select_unmatch_and_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i8 [[A]], i8 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i8 [[B]], i8 [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = and i8 [[S1]], [[S0]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c1, i8 %b, i8 %a
+  %ret = and i8 %s1, %s0
+  ret i8 %ret
+}
+
+define i8 @fold_select_and_neg(i1 %c, i8 %a, i8 %b, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @fold_select_and_neg(
+; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i8 [[A]], i8 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C]], i8 [[Y]], i8 [[Z]]
+; CHECK-NEXT:    [[RET:%.*]] = and i8 [[S1]], [[S0]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c, i8 %y, i8 %z
+  %ret = and i8 %s1, %s0
+  ret i8 %ret
+}
+
+define <2 x i4> @fold_select_and_vec2_neg(i1 %c, <2 x i4> %a, <2 x i4> %b, <2 x i4> %y, <2 x i4> %z) {
+; CHECK-LABEL: define <2 x i4> @fold_select_and_vec2_neg(
+; CHECK-SAME: i1 [[C:%.*]], <2 x i4> [[A:%.*]], <2 x i4> [[B:%.*]], <2 x i4> [[Y:%.*]], <2 x i4> [[Z:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], <2 x i4> [[A]], <2 x i4> [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C]], <2 x i4> [[Y]], <2 x i4> [[Z]]
+; CHECK-NEXT:    [[RET:%.*]] = and <2 x i4> [[S1]], [[S0]]
+; CHECK-NEXT:    ret <2 x i4> [[RET]]
+;
+  %s0 = select i1 %c, <2 x i4> %a, <2 x i4> %b
+  %s1 = select i1 %c, <2 x i4> %y, <2 x i4> %z
+  %ret = and <2 x i4> %s1, %s0
+  ret <2 x i4> %ret
+}
+
+define i8 @fold_select_unmatch_or_neg(i1 %c, i1 %c1, i8 %a, i8 %b) {
+; CHECK-LABEL: define i8 @fold_select_unmatch_or_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i8 [[A]], i8 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i8 [[B]], i8 [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = or i8 [[S1]], [[S0]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c1, i8 %b, i8 %a
+  %ret = or i8 %s1, %s0
+  ret i8 %ret
+}
+
+define i8 @fold_select_or_neg(i1 %c, i8 %a, i8 %b, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @fold_select_or_neg(
+; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = or i8 [[Y]], [[A]]
+; CHECK-NEXT:    [[TMP2:%.*]] = or i8 [[Z]], [[B]]
+; CHECK-NEXT:    [[RET:%.*]] = select i1 [[C]], i8 [[TMP1]], i8 [[TMP2]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c, i8 %y, i8 %z
+  %ret = or i8 %s1, %s0
+  ret i8 %ret
+}
+
+define <2 x i4> @fold_select_or_vec2_neg(i1 %c, <2 x i4> %a, <2 x i4> %b, <2 x i4> %y, <2 x i4> %z) {
+; CHECK-LABEL: define <2 x i4> @fold_select_or_vec2_neg(
+; CHECK-SAME: i1 [[C:%.*]], <2 x i4> [[A:%.*]], <2 x i4> [[B:%.*]], <2 x i4> [[Y:%.*]], <2 x i4> [[Z:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = or <2 x i4> [[Y]], [[A]]
+; CHECK-NEXT:    [[TMP2:%.*]] = or <2 x i4> [[Z]], [[B]]
+; CHECK-NEXT:    [[RET:%.*]] = select i1 [[C]], <2 x i4> [[TMP1]], <2 x i4> [[TMP2]]
+; CHECK-NEXT:    ret <2 x i4> [[RET]]
+;
+  %s0 = select i1 %c, <2 x i4> %a, <2 x i4> %b
+  %s1 = select i1 %c, <2 x i4> %y, <2 x i4> %z
+  %ret = or <2 x i4> %s1, %s0
+  ret <2 x i4> %ret
+}
+
+define i8 @fold_select_unmatch_xor_neg(i1 %c, i1 %c1, i8 %a, i8 %b) {
+; CHECK-LABEL: define i8 @fold_select_unmatch_xor_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i8 [[A]], i8 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i8 [[B]], i8 [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = xor i8 [[S1]], [[S0]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c1, i8 %b, i8 %a
+  %ret = xor i8 %s1, %s0
+  ret i8 %ret
+}
+
+define i8 @fold_select_xor_neg(i1 %c, i8 %a, i8 %b, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @fold_select_xor_neg(
+; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i8 [[A]], i8 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C]], i8 [[Y]], i8 [[Z]]
+; CHECK-NEXT:    [[RET:%.*]] = xor i8 [[S1]], [[S0]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %s0 = select i1 %c, i8 %a, i8 %b
+  %s1 = select i1 %c, i8 %y, i8 %z
+  %ret = xor i8 %s1, %s0
+  ret i8 %ret
+}
+
+define <2 x i4> @fold_select_xor_vec2_neg(i1 %c, <2 x i4> %a, <2 x i4> %b, <2 x i4> %y, <2 x i4> %z) {
+; CHECK-LABEL: define <2 x i4> @fold_select_xor_vec2_neg(
+; CHECK-SAME: i1 [[C:%.*]], <2 x i4> [[A:%.*]], <2 x i4> [[B:%.*]], <2 x i4> [[Y:%.*]], <2 x i4> [[Z:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], <2 x i4> [[A]], <2 x i4> [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C]], <2 x i4> [[Y]], <2 x i4> [[Z]]
+; CHECK-NEXT:    [[RET:%.*]] = xor <2 x i4> [[S1]], [[S0]]
+; CHECK-NEXT:    ret <2 x i4> [[RET]]
+;
+  %s0 = select i1 %c, <2 x i4> %a, <2 x i4> %b
+  %s1 = select i1 %c, <2 x i4> %y, <2 x i4> %z
+  %ret = xor <2 x i4> %s1, %s0
+  ret <2 x i4> %ret
+}
+
+define float @fold_select_unmatch_fadd_neg(i1 %c, i1 %c1, float %a, float %b) {
+; CHECK-LABEL: define float @fold_select_unmatch_fadd_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], float [[A:%.*]], float [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], float [[A]], float [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], float [[B]], float [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = fadd float [[S1]], [[S0]]
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c1, float %b, float %a
+  %ret = fadd float %s1, %s0
+  ret float %ret
+}
+
+define float @fold_select_fadd_neg(i1 %c, float %a, float %b, float %y, float %z) {
+; CHECK-LABEL: define float @fold_select_fadd_neg(
+; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]], float [[Y:%.*]], float [[Z:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], float [[A]], float [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C]], float [[Y]], float [[Z]]
+; CHECK-NEXT:    [[RET:%.*]] = fadd float [[S1]], [[S0]]
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c, float %y, float %z
+  %ret = fadd float %s1, %s0
+  ret float %ret
+}
+
+define <2 x float> @fold_select_fadd_vec2_neg(i1 %c, <2 x float> %a, <2 x float> %b, <2 x float> %y, <2 x float> %z) {
+; CHECK-LABEL: define <2 x float> @fold_select_fadd_vec2_neg(
+; CHECK-SAME: i1 [[C:%.*]], <2 x float> [[A:%.*]], <2 x float> [[B:%.*]], <2 x float> [[Y:%.*]], <2 x float> [[Z:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], <2 x float> [[A]], <2 x float> [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C]], <2 x float> [[Y]], <2 x float> [[Z]]
+; CHECK-NEXT:    [[RET:%.*]] = fadd <2 x float> [[S1]], [[S0]]
+; CHECK-NEXT:    ret <2 x float> [[RET]]
+;
+  %s0 = select i1 %c, <2 x float> %a, <2 x float> %b
+  %s1 = select i1 %c, <2 x float> %y, <2 x float> %z
+  %ret = fadd <2 x float> %s1, %s0
+  ret <2 x float> %ret
+}
+
+define float @fold_select_unmatch_fmul_neg(i1 %c, i1 %c1, float %a, float %b) {
+; CHECK-LABEL: define float @fold_select_unmatch_fmul_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], float [[A:%.*]], float [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], float [[A]], float [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], float [[B]], float [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = fmul float [[S1]], [[S0]]
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c1, float %b, float %a
+  %ret = fmul float %s1, %s0
+  ret float %ret
+}
+
+define float @fold_select_fmul_neg(i1 %c, float %a, float %b, float %y, float %z) {
+; CHECK-LABEL: define float @fold_select_fmul_neg(
+; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]], float [[Y:%.*]], float [[Z:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], float [[A]], float [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C]], float [[Y]], float [[Z]]
+; CHECK-NEXT:    [[RET:%.*]] = fmul float [[S1]], [[S0]]
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c, float %y, float %z
+  %ret = fmul float %s1, %s0
+  ret float %ret
+}
+
+define <2 x float> @fold_select_fmul_vec2_neg(i1 %c, <2 x float> %a, <2 x float> %b, <2 x float> %y, <2 x float> %z) {
+; CHECK-LABEL: define <2 x float> @fold_select_fmul_vec2_neg(
+; CHECK-SAME: i1 [[C:%.*]], <2 x float> [[A:%.*]], <2 x float> [[B:%.*]], <2 x float> [[Y:%.*]], <2 x float> [[Z:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], <2 x float> [[A]], <2 x float> [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C]], <2 x float> [[Y]], <2 x float> [[Z]]
+; CHECK-NEXT:    [[RET:%.*]] = fmul <2 x float> [[S1]], [[S0]]
+; CHECK-NEXT:    ret <2 x float> [[RET]]
+;
+  %s0 = select i1 %c, <2 x float> %a, <2 x float> %b
+  %s1 = select i1 %c, <2 x float> %y, <2 x float> %z
+  %ret = fmul <2 x float> %s1, %s0
+  ret <2 x float> %ret
+}
+
+define float @fold_select_maxnum_neg(i1 %c, i1 %c1, float %a, float %b) {
+; CHECK-LABEL: define float @fold_select_maxnum_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], float [[A:%.*]], float [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], float [[A]], float [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], float [[B]], float [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.maxnum.f32(float [[S1]], float [[S0]])
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c1, float %b, float %a
+  %ret = call float @llvm.maxnum.f32(float %s1, float %s0)
+  ret float %ret
+}
+
+define float @fold_select_minnum_neg(i1 %c, i1 %c1, float %a, float %b) {
+; CHECK-LABEL: define float @fold_select_minnum_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], float [[A:%.*]], float [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], float [[A]], float [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], float [[B]], float [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.minnum.f32(float [[S1]], float [[S0]])
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c1, float %b, float %a
+  %ret = call float @llvm.minnum.f32(float %s1, float %s0)
+  ret float %ret
+}
+
+define float @fold_select_maximum_neg(i1 %c, i1 %c1, float %a, float %b) {
+; CHECK-LABEL: define float @fold_select_maximum_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], float [[A:%.*]], float [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], float [[A]], float [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], float [[B]], float [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.maximum.f32(float [[S1]], float [[S0]])
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c1, float %b, float %a
+  %ret = call float @llvm.maximum.f32(float %s1, float %s0)
+  ret float %ret
+}
+
+define float @fold_select_minimum_neg(i1 %c, i1 %c1, float %a, float %b) {
+; CHECK-LABEL: define float @fold_select_minimum_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], float [[A:%.*]], float [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], float [[A]], float [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], float [[B]], float [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.minimum.f32(float [[S1]], float [[S0]])
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c1, float %b, float %a
+  %ret = call float @llvm.minimum.f32(float %s1, float %s0)
+  ret float %ret
+}
+
+define i32 @fold_select_smax_neg(i1 %c, i1 %c1, i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @fold_select_smax_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i32 [[A]], i32 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i32 [[B]], i32 [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call i32 @llvm.smax.i32(i32 [[S1]], i32 [[S0]])
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %s0 = select i1 %c, i32 %a, i32 %b
+  %s1 = select i1 %c1, i32 %b, i32 %a
+  %ret = call i32 @llvm.smax.i32(i32 %s1, i32 %s0)
+  ret i32 %ret
+}
+
+define i32 @fold_select_smin_neg(i1 %c, i1 %c1, i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @fold_select_smin_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i32 [[A]], i32 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i32 [[B]], i32 [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call i32 @llvm.smin.i32(i32 [[S1]], i32 [[S0]])
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %s0 = select i1 %c, i32 %a, i32 %b
+  %s1 = select i1 %c1, i32 %b, i32 %a
+  %ret = call i32 @llvm.smin.i32(i32 %s1, i32 %s0)
+  ret i32 %ret
+}
+
+define i32 @fold_select_umax_neg(i1 %c, i1 %c1, i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @fold_select_umax_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i32 [[A]], i32 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i32 [[B]], i32 [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call i32 @llvm.umax.i32(i32 [[S1]], i32 [[S0]])
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %s0 = select i1 %c, i32 %a, i32 %b
+  %s1 = select i1 %c1, i32 %b, i32 %a
+  %ret = call i32 @llvm.umax.i32(i32 %s1, i32 %s0)
+  ret i32 %ret
+}
+
+define i32 @fold_select_umin_neg(i1 %c, i1 %c1, i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @fold_select_umin_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i32 [[A]], i32 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i32 [[B]], i32 [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call i32 @llvm.umin.i32(i32 [[S1]], i32 [[S0]])
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %s0 = select i1 %c, i32 %a, i32 %b
+  %s1 = select i1 %c1, i32 %b, i32 %a
+  %ret = call i32 @llvm.umin.i32(i32 %s1, i32 %s0)
+  ret i32 %ret
+}
+
+define i16 @fold_select_sadd_sat_neg(i1 %c, i1 %c1, i16 %a, i16 %b) {
+; CHECK-LABEL: define i16 @fold_select_sadd_sat_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i16 [[A:%.*]], i16 [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i16 [[A]], i16 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i16 [[B]], i16 [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call i16 @llvm.sadd.sat.i16(i16 [[S1]], i16 [[S0]])
+; CHECK-NEXT:    ret i16 [[RET]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c1, i16 %b, i16 %a
+  %ret = call i16 @llvm.sadd.sat.i16(i16 %s1, i16 %s0)
+  ret i16 %ret
+}
+
+define i16 @fold_select_uadd_sat_neg(i1 %c, i1 %c1, i16 %a, i16 %b) {
+; CHECK-LABEL: define i16 @fold_select_uadd_sat_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i16 [[A:%.*]], i16 [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i16 [[A]], i16 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i16 [[B]], i16 [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call i16 @llvm.uadd.sat.i16(i16 [[S1]], i16 [[S0]])
+; CHECK-NEXT:    ret i16 [[RET]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c1, i16 %b, i16 %a
+  %ret = call i16 @llvm.uadd.sat.i16(i16 %s1, i16 %s0)
+  ret i16 %ret
+}
+
+define i16 @fold_select_sadd_with_overflow_neg(i1 %c, i1 %c1, i16 %a, i16 %b) {
+; CHECK-LABEL: define i16 @fold_select_sadd_with_overflow_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i16 [[A:%.*]], i16 [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i16 [[A]], i16 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i16 [[B]], i16 [[A]]
+; CHECK-NEXT:    [[RES:%.*]] = call { i16, i1 } @llvm.sadd.with.overflow.i16(i16 [[S1]], i16 [[S0]])
+; CHECK-NEXT:    [[OBIT:%.*]] = extractvalue { i16, i1 } [[RES]], 1
+; CHECK-NEXT:    br i1 [[OBIT]], label [[OVERFLOW:%.*]], label [[NORMAL:%.*]]
+; CHECK:       overflow:
+; CHECK-NEXT:    ret i16 0
+; CHECK:       normal:
+; CHECK-NEXT:    [[SUM:%.*]] = extractvalue { i16, i1 } [[RES]], 0
+; CHECK-NEXT:    ret i16 [[SUM]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c1, i16 %b, i16 %a
+  %res = call {i16, i1} @llvm.sadd.with.overflow.i16(i16 %s1, i16 %s0)
+  %obit = extractvalue {i16, i1} %res, 1
+  br i1 %obit, label %overflow, label %normal
+overflow:
+  ret i16 0
+normal:
+  %sum = extractvalue {i16, i1} %res, 0
+  ret i16 %sum
+}
+
+define i16 @fold_select_uadd_with_overflow_neg(i1 %c, i1 %c1, i16 %a, i16 %b) {
+; CHECK-LABEL: define i16 @fold_select_uadd_with_overflow_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i16 [[A:%.*]], i16 [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i16 [[A]], i16 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i16 [[B]], i16 [[A]]
+; CHECK-NEXT:    [[RES:%.*]] = call { i16, i1 } @llvm.uadd.with.overflow.i16(i16 [[S1]], i16 [[S0]])
+; CHECK-NEXT:    [[OBIT:%.*]] = extractvalue { i16, i1 } [[RES]], 1
+; CHECK-NEXT:    br i1 [[OBIT]], label [[OVERFLOW:%.*]], label [[NORMAL:%.*]]
+; CHECK:       overflow:
+; CHECK-NEXT:    ret i16 0
+; CHECK:       normal:
+; CHECK-NEXT:    [[SUM:%.*]] = extractvalue { i16, i1 } [[RES]], 0
+; CHECK-NEXT:    ret i16 [[SUM]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c1, i16 %b, i16 %a
+  %res = call {i16, i1} @llvm.uadd.with.overflow.i16(i16 %s1, i16 %s0)
+  %obit = extractvalue {i16, i1} %res, 1
+  br i1 %obit, label %overflow, label %normal
+overflow:
+  ret i16 0
+normal:
+  %sum = extractvalue {i16, i1} %res, 0
+  ret i16 %sum
+}
+
+define i16 @fold_select_smul_with_overflow_neg(i1 %c, i1 %c1, i16 %a, i16 %b) {
+; CHECK-LABEL: define i16 @fold_select_smul_with_overflow_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i16 [[A:%.*]], i16 [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i16 [[A]], i16 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i16 [[B]], i16 [[A]]
+; CHECK-NEXT:    [[RES:%.*]] = call { i16, i1 } @llvm.smul.with.overflow.i16(i16 [[S1]], i16 [[S0]])
+; CHECK-NEXT:    [[OBIT:%.*]] = extractvalue { i16, i1 } [[RES]], 1
+; CHECK-NEXT:    br i1 [[OBIT]], label [[OVERFLOW:%.*]], label [[NORMAL:%.*]]
+; CHECK:       overflow:
+; CHECK-NEXT:    ret i16 0
+; CHECK:       normal:
+; CHECK-NEXT:    [[MUL:%.*]] = extractvalue { i16, i1 } [[RES]], 0
+; CHECK-NEXT:    ret i16 [[MUL]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c1, i16 %b, i16 %a
+  %res = call {i16, i1} @llvm.smul.with.overflow.i16(i16 %s1, i16 %s0)
+  %obit = extractvalue {i16, i1} %res, 1
+  br i1 %obit, label %overflow, label %normal
+overflow:
+  ret i16 0
+normal:
+  %mul = extractvalue {i16, i1} %res, 0
+  ret i16 %mul
+}
+
+define i16 @fold_select_umul_with_overflow_neg(i1 %c, i1 %c1, i16 %a, i16 %b) {
+; CHECK-LABEL: define i16 @fold_select_umul_with_overflow_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i16 [[A:%.*]], i16 [[B:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i16 [[A]], i16 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i16 [[B]], i16 [[A]]
+; CHECK-NEXT:    [[RES:%.*]] = call { i16, i1 } @llvm.umul.with.overflow.i16(i16 [[S1]], i16 [[S0]])
+; CHECK-NEXT:    [[OBIT:%.*]] = extractvalue { i16, i1 } [[RES]], 1
+; CHECK-NEXT:    br i1 [[OBIT]], label [[OVERFLOW:%.*]], label [[NORMAL:%.*]]
+; CHECK:       overflow:
+; CHECK-NEXT:    ret i16 0
+; CHECK:       normal:
+; CHECK-NEXT:    [[MUL:%.*]] = extractvalue { i16, i1 } [[RES]], 0
+; CHECK-NEXT:    ret i16 [[MUL]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c1, i16 %b, i16 %a
+  %res = call {i16, i1} @llvm.umul.with.overflow.i16(i16 %s1, i16 %s0)
+  %obit = extractvalue {i16, i1} %res, 1
+  br i1 %obit, label %overflow, label %normal
+overflow:
+  ret i16 0
+normal:
+  %mul = extractvalue {i16, i1} %res, 0
+  ret i16 %mul
+}
+
+define i16 @fold_select_smul_fix_neg(i1 %c, i1 %c1, i16 %a, i16 %b, i32 %y) {
+; CHECK-LABEL: define i16 @fold_select_smul_fix_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i16 [[A:%.*]], i16 [[B:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i16 [[A]], i16 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i16 [[B]], i16 [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call i16 @llvm.smul.fix.i16(i16 [[S1]], i16 [[S0]], i32 1)
+; CHECK-NEXT:    ret i16 [[RET]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c1, i16 %b, i16 %a
+  %ret = call i16 @llvm.smul.fix.i16(i16 %s1, i16 %s0, i32 1)
+  ret i16 %ret
+}
+
+define i16 @fold_select_umul_fix_neg(i1 %c, i1 %c1, i16 %a, i16 %b, i32 %y) {
+; CHECK-LABEL: define i16 @fold_select_umul_fix_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i16 [[A:%.*]], i16 [[B:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i16 [[A]], i16 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i16 [[B]], i16 [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call i16 @llvm.umul.fix.i16(i16 [[S1]], i16 [[S0]], i32 1)
+; CHECK-NEXT:    ret i16 [[RET]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c1, i16 %b, i16 %a
+  %ret = call i16 @llvm.umul.fix.i16(i16 %s1, i16 %s0, i32 1)
+  ret i16 %ret
+}
+
+define i16 @fold_select_smul_fix_sat_neg(i1 %c, i1 %c1, i16 %a, i16 %b, i32 %y) {
+; CHECK-LABEL: define i16 @fold_select_smul_fix_sat_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i16 [[A:%.*]], i16 [[B:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i16 [[A]], i16 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i16 [[B]], i16 [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call i16 @llvm.smul.fix.sat.i16(i16 [[S1]], i16 [[S0]], i32 1)
+; CHECK-NEXT:    ret i16 [[RET]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c1, i16 %b, i16 %a
+  %ret = call i16 @llvm.smul.fix.sat.i16(i16 %s1, i16 %s0, i32 1)
+  ret i16 %ret
+}
+
+define i16 @fold_select_umul_fix_sat_neg(i1 %c, i1 %c1, i16 %a, i16 %b, i32 %y) {
+; CHECK-LABEL: define i16 @fold_select_umul_fix_sat_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], i16 [[A:%.*]], i16 [[B:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], i16 [[A]], i16 [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i16 [[B]], i16 [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call i16 @llvm.umul.fix.sat.i16(i16 [[S1]], i16 [[S0]], i32 1)
+; CHECK-NEXT:    ret i16 [[RET]]
+;
+  %s0 = select i1 %c, i16 %a, i16 %b
+  %s1 = select i1 %c1, i16 %b, i16 %a
+  %ret = call i16 @llvm.umul.fix.sat.i16(i16 %s1, i16 %s0, i32 1)
+  ret i16 %ret
+}
+
+define float @fold_select_fma_neg(i1 %c, i1 %c1, float %a, float %b, float %y) {
+; CHECK-LABEL: define float @fold_select_fma_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], float [[A:%.*]], float [[B:%.*]], float [[Y:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], float [[A]], float [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], float [[B]], float [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.fma.f32(float [[S1]], float [[S0]], float [[Y]])
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c1, float %b, float %a
+  %ret = call float @llvm.fma.f32(float %s1, float %s0, float %y)
+  ret float %ret
+}
+
+define float @fold_select_fmuladd_neg(i1 %c, i1 %c1, float %a, float %b, float %y) {
+; CHECK-LABEL: define float @fold_select_fmuladd_neg(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C1:%.*]], float [[A:%.*]], float [[B:%.*]], float [[Y:%.*]]) {
+; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C]], float [[A]], float [[B]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], float [[B]], float [[A]]
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.fmuladd.f32(float [[S1]], float [[S0]], float [[Y]])
+; CHECK-NEXT:    ret float [[RET]]
+;
+  %s0 = select i1 %c, float %a, float %b
+  %s1 = select i1 %c1, float %b, float %a
+  %ret = call float @llvm.fmuladd.f32(float %s1, float %s0, float %y)
+  ret float %ret
+}



More information about the llvm-commits mailing list