diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index c496f9c7419b5..a272357fa04a4 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -1539,6 +1539,9 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) { if (Instruction *I = foldCommutativeIntrinsicOverSelects(*II)) return I; + if (Instruction *I = foldCommutativeIntrinsicOverPhis(*II)) + return I; + if (CallInst *NewCall = canonicalizeConstantArg0ToArg1(CI)) return NewCall; } @@ -4237,3 +4240,22 @@ InstCombinerImpl::foldCommutativeIntrinsicOverSelects(IntrinsicInst &II) { return nullptr; } + +Instruction * +InstCombinerImpl::foldCommutativeIntrinsicOverPhis(IntrinsicInst &II) { + assert(II.isCommutative() && "Instruction should be commutative"); + + PHINode *LHS = dyn_cast(II.getOperand(0)); + PHINode *RHS = dyn_cast(II.getOperand(1)); + + if (!LHS || !RHS) + return nullptr; + + if (auto P = matchSymmetricPhiNodesPair(LHS, RHS)) { + replaceOperand(II, 0, P->first); + replaceOperand(II, 1, P->second); + return &II; + } + + return nullptr; +} diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h index f86db698ef8f1..9e76a0cf17b18 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -278,6 +278,16 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final IntrinsicInst &Tramp); Instruction *foldCommutativeIntrinsicOverSelects(IntrinsicInst &II); + // Match a pair of Phi Nodes like + // phi [a, BB0], [b, BB1] & phi [b, BB0], [a, BB1] + // Return the matched two operands. + std::optional> + matchSymmetricPhiNodesPair(PHINode *LHS, PHINode *RHS); + + // Tries to fold (op phi(a, b) phi(b, a)) -> (op a, b) + // while op is a commutative intrinsic call. + Instruction *foldCommutativeIntrinsicOverPhis(IntrinsicInst &II); + Value *simplifyMaskedLoad(IntrinsicInst &II); Instruction *simplifyMaskedStore(IntrinsicInst &II); Instruction *simplifyMaskedGather(IntrinsicInst &II); @@ -492,6 +502,11 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final /// X % (C0 * C1) Value *SimplifyAddWithRemainder(BinaryOperator &I); + // Tries to fold (Binop phi(a, b) phi(b, a)) -> (Binop a, b) + // while Binop is commutative. + Value *SimplifyPhiCommutativeBinaryOp(BinaryOperator &I, Value *LHS, + Value *RHS); + // Binary Op helper for select operations where the expression can be // efficiently reorganized. Value *SimplifySelectsFeedingBinaryOp(BinaryOperator &I, Value *LHS, diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp index 4188b5b46e87e..775720ab43a5c 100644 --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -1096,6 +1096,54 @@ Value *InstCombinerImpl::foldUsingDistributiveLaws(BinaryOperator &I) { return SimplifySelectsFeedingBinaryOp(I, LHS, RHS); } +std::optional> +InstCombinerImpl::matchSymmetricPhiNodesPair(PHINode *LHS, PHINode *RHS) { + if (LHS->getParent() != RHS->getParent()) + return std::nullopt; + + if (LHS->getNumIncomingValues() < 2) + return std::nullopt; + + if (!equal(LHS->blocks(), RHS->blocks())) + return std::nullopt; + + Value *L0 = LHS->getIncomingValue(0); + Value *R0 = RHS->getIncomingValue(0); + + for (unsigned I = 1, E = LHS->getNumIncomingValues(); I != E; ++I) { + Value *L1 = LHS->getIncomingValue(I); + Value *R1 = RHS->getIncomingValue(I); + + if ((L0 == L1 && R0 == R1) || (L0 == R1 && R0 == L1)) + continue; + + return std::nullopt; + } + + return std::optional(std::pair(L0, R0)); +} + +Value *InstCombinerImpl::SimplifyPhiCommutativeBinaryOp(BinaryOperator &I, + Value *Op0, + Value *Op1) { + assert(I.isCommutative() && "Instruction should be commutative"); + + PHINode *LHS = dyn_cast(Op0); + PHINode *RHS = dyn_cast(Op1); + + if (!LHS || !RHS) + return nullptr; + + if (auto P = matchSymmetricPhiNodesPair(LHS, RHS)) { + Value *BI = Builder.CreateBinOp(I.getOpcode(), P->first, P->second); + if (auto *BO = dyn_cast(BI)) + BO->copyIRFlags(&I); + return BI; + } + + return nullptr; +} + Value *InstCombinerImpl::SimplifySelectsFeedingBinaryOp(BinaryOperator &I, Value *LHS, Value *RHS) { @@ -1529,6 +1577,11 @@ Instruction *InstCombinerImpl::foldBinopWithPhiOperands(BinaryOperator &BO) { BO.getParent() != Phi1->getParent()) return nullptr; + if (BO.isCommutative()) { + if (Value *V = SimplifyPhiCommutativeBinaryOp(BO, Phi0, Phi1)) + return replaceInstUsesWith(BO, V); + } + // Fold if there is at least one specific constant value in phi0 or phi1's // incoming values that comes from the same block and this specific constant // value can be used to do optimization for specific binary operator. diff --git a/llvm/test/Transforms/InstCombine/commutative-operation-over-phis.ll b/llvm/test/Transforms/InstCombine/commutative-operation-over-phis.ll new file mode 100644 index 0000000000000..e8b0fb198bd11 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/commutative-operation-over-phis.ll @@ -0,0 +1,645 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 +; RUN: opt -passes=instcombine -S < %s | FileCheck %s + +declare void @dummy() + +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 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 float @llvm.pow.f32(float %a, float %b) + +define i8 @fold_phi_mul(i1 %c, i8 %a, i8 %b) { +; CHECK-LABEL: define i8 @fold_phi_mul( +; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = mul i8 [[A]], [[B]] +; CHECK-NEXT: ret i8 [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi i8 [%a, %entry], [%b, %then] + %phi2 = phi i8 [%b, %entry], [%a, %then] + %ret = mul i8 %phi1, %phi2 + ret i8 %ret +} + +define i8 @fold_phi_mul_three(i1 %c, i1 %d, i8 %a, i8 %b) { +; CHECK-LABEL: define i8 @fold_phi_mul_three( +; CHECK-SAME: i1 [[C:%.*]], i1 [[D:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN1:%.*]], label [[END:%.*]] +; CHECK: then1: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br i1 [[D]], label [[THEN2:%.*]], label [[END]] +; CHECK: then2: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = mul i8 [[A]], [[B]] +; CHECK-NEXT: ret i8 [[RET]] +; +entry: + br i1 %c, label %then1, label %end +then1: + call void @dummy() + br i1 %d, label %then2, label %end +then2: + call void @dummy() + br label %end +end: + %phi1 = phi i8 [%a, %entry], [%b, %then1], [%a, %then2] + %phi2 = phi i8 [%b, %entry], [%a, %then1], [%b, %then2] + %ret = mul i8 %phi1, %phi2 + ret i8 %ret +} + +define i8 @fold_phi_mul_three_notopt(i1 %c, i1 %d, i8 %a, i8 %b) { +; CHECK-LABEL: define i8 @fold_phi_mul_three_notopt( +; CHECK-SAME: i1 [[C:%.*]], i1 [[D:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN1:%.*]], label [[END:%.*]] +; CHECK: then1: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br i1 [[D]], label [[THEN2:%.*]], label [[END]] +; CHECK: then2: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[PHI1:%.*]] = phi i8 [ [[A]], [[ENTRY:%.*]] ], [ [[B]], [[THEN1]] ], [ [[A]], [[THEN2]] ] +; CHECK-NEXT: [[PHI2:%.*]] = phi i8 [ [[B]], [[ENTRY]] ], [ [[A]], [[THEN1]] ], [ [[A]], [[THEN2]] ] +; CHECK-NEXT: [[RET:%.*]] = mul i8 [[PHI1]], [[PHI2]] +; CHECK-NEXT: ret i8 [[RET]] +; +entry: + br i1 %c, label %then1, label %end +then1: + call void @dummy() + br i1 %d, label %then2, label %end +then2: + call void @dummy() + br label %end +end: + %phi1 = phi i8 [%a, %entry], [%b, %then1], [%a, %then2] + %phi2 = phi i8 [%b, %entry], [%a, %then1], [%a, %then2] + %ret = mul i8 %phi1, %phi2 + ret i8 %ret +} + +define i8 @fold_phi_mul_nsw_nuw(i1 %c, i8 %a, i8 %b) { +; CHECK-LABEL: define i8 @fold_phi_mul_nsw_nuw( +; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = mul nuw nsw i8 [[A]], [[B]] +; CHECK-NEXT: ret i8 [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi i8 [%a, %entry], [%b, %then] + %phi2 = phi i8 [%b, %entry], [%a, %then] + %ret = mul nsw nuw i8 %phi1, %phi2 + ret i8 %ret +} + +define <2 x i8> @fold_phi_mul_fix_vec(i1 %c, <2 x i8> %a, <2 x i8> %b) { +; CHECK-LABEL: define <2 x i8> @fold_phi_mul_fix_vec( +; CHECK-SAME: i1 [[C:%.*]], <2 x i8> [[A:%.*]], <2 x i8> [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = mul <2 x i8> [[A]], [[B]] +; CHECK-NEXT: ret <2 x i8> [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi <2 x i8> [%a, %entry], [%b, %then] + %phi2 = phi <2 x i8> [%b, %entry], [%a, %then] + %ret = mul <2 x i8> %phi1, %phi2 + ret <2 x i8> %ret +} + +define @fold_phi_mul_scale_vec(i1 %c, %a, %b) { +; CHECK-LABEL: define @fold_phi_mul_scale_vec( +; CHECK-SAME: i1 [[C:%.*]], [[A:%.*]], [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = mul [[A]], [[B]] +; CHECK-NEXT: ret [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi [%a, %entry], [%b, %then] + %phi2 = phi [%b, %entry], [%a, %then] + %ret = mul %phi1, %phi2 + ret %ret +} + +define i8 @fold_phi_mul_commute(i1 %c, i8 %a, i8 %b) { +; CHECK-LABEL: define i8 @fold_phi_mul_commute( +; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = mul i8 [[A]], [[B]] +; CHECK-NEXT: ret i8 [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi i8 [%a, %entry], [%b, %then] + %phi2 = phi i8 [%a, %then], [%b, %entry] + %ret = mul i8 %phi1, %phi2 + ret i8 %ret +} + + +define i8 @fold_phi_mul_notopt(i1 %c, i8 %a, i8 %b, i8 %d) { +; CHECK-LABEL: define i8 @fold_phi_mul_notopt( +; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]], i8 [[D:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[PHI1:%.*]] = phi i8 [ [[A]], [[ENTRY:%.*]] ], [ [[B]], [[THEN]] ] +; CHECK-NEXT: [[PHI2:%.*]] = phi i8 [ [[B]], [[ENTRY]] ], [ [[D]], [[THEN]] ] +; CHECK-NEXT: [[RET:%.*]] = mul i8 [[PHI1]], [[PHI2]] +; CHECK-NEXT: ret i8 [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi i8 [%a, %entry], [%b, %then] + %phi2 = phi i8 [%b, %entry], [%d, %then] + %ret = mul i8 %phi1, %phi2 + ret i8 %ret +} + + +define i8 @fold_phi_sub(i1 %c, i8 %a, i8 %b) { +; CHECK-LABEL: define i8 @fold_phi_sub( +; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[PHI1:%.*]] = phi i8 [ [[A]], [[ENTRY:%.*]] ], [ [[B]], [[THEN]] ] +; CHECK-NEXT: [[PHI2:%.*]] = phi i8 [ [[B]], [[ENTRY]] ], [ [[A]], [[THEN]] ] +; CHECK-NEXT: [[RET:%.*]] = sub i8 [[PHI1]], [[PHI2]] +; CHECK-NEXT: ret i8 [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi i8 [%a, %entry], [%b, %then] + %phi2 = phi i8 [%b, %entry], [%a, %then] + %ret = sub i8 %phi1, %phi2 + ret i8 %ret +} + + +define i8 @fold_phi_add(i1 %c, i8 %a, i8 %b) { +; CHECK-LABEL: define i8 @fold_phi_add( +; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = add i8 [[A]], [[B]] +; CHECK-NEXT: ret i8 [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi i8 [%a, %entry], [%b, %then] + %phi2 = phi i8 [%b, %entry], [%a, %then] + %ret = add i8 %phi1, %phi2 + ret i8 %ret +} + +define i8 @fold_phi_and(i1 %c, i8 %a, i8 %b) { +; CHECK-LABEL: define i8 @fold_phi_and( +; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = and i8 [[A]], [[B]] +; CHECK-NEXT: ret i8 [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi i8 [%a, %entry], [%b, %then] + %phi2 = phi i8 [%b, %entry], [%a, %then] + %ret = and i8 %phi1, %phi2 + ret i8 %ret +} + +define i8 @fold_phi_or(i1 %c, i8 %a, i8 %b) { +; CHECK-LABEL: define i8 @fold_phi_or( +; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = or i8 [[A]], [[B]] +; CHECK-NEXT: ret i8 [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi i8 [%a, %entry], [%b, %then] + %phi2 = phi i8 [%b, %entry], [%a, %then] + %ret = or i8 %phi1, %phi2 + ret i8 %ret +} + + +define i8 @fold_phi_xor(i1 %c, i8 %a, i8 %b) { +; CHECK-LABEL: define i8 @fold_phi_xor( +; CHECK-SAME: i1 [[C:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = xor i8 [[A]], [[B]] +; CHECK-NEXT: ret i8 [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi i8 [%a, %entry], [%b, %then] + %phi2 = phi i8 [%b, %entry], [%a, %then] + %ret = xor i8 %phi1, %phi2 + ret i8 %ret +} + + +define float @fold_phi_fadd(i1 %c, float %a, float %b) { +; CHECK-LABEL: define float @fold_phi_fadd( +; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = fadd float [[A]], [[B]] +; CHECK-NEXT: ret float [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi float [%a, %entry], [%b, %then] + %phi2 = phi float [%b, %entry], [%a, %then] + %ret = fadd float %phi1, %phi2 + ret float %ret +} + +define float @fold_phi_fadd_nnan(i1 %c, float %a, float %b) { +; CHECK-LABEL: define float @fold_phi_fadd_nnan( +; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = fadd nnan float [[A]], [[B]] +; CHECK-NEXT: ret float [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi float [%a, %entry], [%b, %then] + %phi2 = phi float [%b, %entry], [%a, %then] + %ret = fadd nnan float %phi1, %phi2 + ret float %ret +} + + +define float @fold_phi_fmul(i1 %c, float %a, float %b) { +; CHECK-LABEL: define float @fold_phi_fmul( +; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = fmul float [[A]], [[B]] +; CHECK-NEXT: ret float [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi float [%a, %entry], [%b, %then] + %phi2 = phi float [%b, %entry], [%a, %then] + %ret = fmul float %phi1, %phi2 + ret float %ret +} + + +define i32 @fold_phi_smax(i1 %c, i32 %a, i32 %b) { +; CHECK-LABEL: define i32 @fold_phi_smax( +; CHECK-SAME: i1 [[C:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = call i32 @llvm.smax.i32(i32 [[A]], i32 [[B]]) +; CHECK-NEXT: ret i32 [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi i32 [%a, %entry], [%b, %then] + %phi2 = phi i32 [%b, %entry], [%a, %then] + %ret = call i32 @llvm.smax.i32(i32 %phi1, i32 %phi2) + ret i32 %ret +} + + +define i32 @fold_phi_smin(i1 %c, i32 %a, i32 %b) { +; CHECK-LABEL: define i32 @fold_phi_smin( +; CHECK-SAME: i1 [[C:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = call i32 @llvm.smin.i32(i32 [[A]], i32 [[B]]) +; CHECK-NEXT: ret i32 [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi i32 [%a, %entry], [%b, %then] + %phi2 = phi i32 [%b, %entry], [%a, %then] + %ret = call i32 @llvm.smin.i32(i32 %phi1, i32 %phi2) + ret i32 %ret +} + + +define i32 @fold_phi_umax(i1 %c, i32 %a, i32 %b) { +; CHECK-LABEL: define i32 @fold_phi_umax( +; CHECK-SAME: i1 [[C:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = call i32 @llvm.umax.i32(i32 [[A]], i32 [[B]]) +; CHECK-NEXT: ret i32 [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi i32 [%a, %entry], [%b, %then] + %phi2 = phi i32 [%b, %entry], [%a, %then] + %ret = call i32 @llvm.umax.i32(i32 %phi1, i32 %phi2) + ret i32 %ret +} + +define i32 @fold_phi_umin(i1 %c, i32 %a, i32 %b) { +; CHECK-LABEL: define i32 @fold_phi_umin( +; CHECK-SAME: i1 [[C:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = call i32 @llvm.umin.i32(i32 [[A]], i32 [[B]]) +; CHECK-NEXT: ret i32 [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi i32 [%a, %entry], [%b, %then] + %phi2 = phi i32 [%b, %entry], [%a, %then] + %ret = call i32 @llvm.umin.i32(i32 %phi1, i32 %phi2) + ret i32 %ret +} + + +define float @fold_phi_maxnum(i1 %c, float %a, float %b) { +; CHECK-LABEL: define float @fold_phi_maxnum( +; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = call float @llvm.maxnum.f32(float [[A]], float [[B]]) +; CHECK-NEXT: ret float [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi float [%a, %entry], [%b, %then] + %phi2 = phi float [%b, %entry], [%a, %then] + %ret = call float @llvm.maxnum.f32(float %phi1, float %phi2) + ret float %ret +} + +define float @fold_phi_pow(i1 %c, float %a, float %b) { +; CHECK-LABEL: define float @fold_phi_pow( +; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[PHI1:%.*]] = phi float [ [[A]], [[ENTRY:%.*]] ], [ [[B]], [[THEN]] ] +; CHECK-NEXT: [[PHI2:%.*]] = phi float [ [[B]], [[ENTRY]] ], [ [[A]], [[THEN]] ] +; CHECK-NEXT: [[RET:%.*]] = call float @llvm.pow.f32(float [[PHI1]], float [[PHI2]]) +; CHECK-NEXT: ret float [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi float [%a, %entry], [%b, %then] + %phi2 = phi float [%b, %entry], [%a, %then] + %ret = call float @llvm.pow.f32(float %phi1, float %phi2) + ret float %ret +} + +define float @fold_phi_minnum(i1 %c, float %a, float %b) { +; CHECK-LABEL: define float @fold_phi_minnum( +; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = call float @llvm.minnum.f32(float [[A]], float [[B]]) +; CHECK-NEXT: ret float [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi float [%a, %entry], [%b, %then] + %phi2 = phi float [%b, %entry], [%a, %then] + %ret = call float @llvm.minnum.f32(float %phi1, float %phi2) + ret float %ret +} + +define float @fold_phi_maximum(i1 %c, float %a, float %b) { +; CHECK-LABEL: define float @fold_phi_maximum( +; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = call float @llvm.maximum.f32(float [[A]], float [[B]]) +; CHECK-NEXT: ret float [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi float [%a, %entry], [%b, %then] + %phi2 = phi float [%b, %entry], [%a, %then] + %ret = call float @llvm.maximum.f32(float %phi1, float %phi2) + ret float %ret +} + +define float @fold_phi_minimum(i1 %c, float %a, float %b) { +; CHECK-LABEL: define float @fold_phi_minimum( +; CHECK-SAME: i1 [[C:%.*]], float [[A:%.*]], float [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[END:%.*]] +; CHECK: then: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[RET:%.*]] = call float @llvm.minimum.f32(float [[A]], float [[B]]) +; CHECK-NEXT: ret float [[RET]] +; +entry: + br i1 %c, label %then, label %end +then: + call void @dummy() + br label %end +end: + %phi1 = phi float [%a, %entry], [%b, %then] + %phi2 = phi float [%b, %entry], [%a, %then] + %ret = call float @llvm.minimum.f32(float %phi1, float %phi2) + ret float %ret +} +