diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -2317,6 +2317,48 @@ return IntrinsicInst::Create(F, { TVal, TVal, ShAmt }); } +static Instruction *foldSelectToCopysign(SelectInst &Sel, + InstCombiner::BuilderTy &Builder) { + Value *Cond = Sel.getCondition(); + Value *TVal = Sel.getTrueValue(); + Value *FVal = Sel.getFalseValue(); + Type *SelType = Sel.getType(); + + // Match select ?, TC, FC where the constants are equal but negated. + // TODO: Generalize to handle a negated variable operand? + const APFloat *TC, *FC; + if (!match(TVal, m_APFloat(TC)) || !match(FVal, m_APFloat(FC)) || + !abs(*TC).bitwiseIsEqual(abs(*FC))) + return nullptr; + + assert(TC != FC && "Expected equal select arms to simplify"); + + Value *X; + const APInt *C; + bool IsTrueIfSignSet; + ICmpInst::Predicate Pred; + if (!match(Cond, m_OneUse(m_ICmp(Pred, m_BitCast(m_Value(X)), m_APInt(C)))) || + !isSignBitCheck(Pred, *C, IsTrueIfSignSet) || X->getType() != SelType) + return nullptr; + + // If needed, negate the value that will be the sign argument of the copysign: + // (bitcast X) < 0 ? -TC : TC --> copysign(TC, X) + // (bitcast X) < 0 ? TC : -TC --> copysign(TC, -X) + // (bitcast X) >= 0 ? -TC : TC --> copysign(TC, -X) + // (bitcast X) >= 0 ? TC : -TC --> copysign(TC, X) + if (IsTrueIfSignSet ^ TC->isNegative()) + X = Builder.CreateFNegFMF(X, &Sel); + + // Canonicalize the magnitude argument as the positive constant since we do + // not care about its sign. + Value *MagArg = TC->isNegative() ? FVal : TVal; + Function *F = Intrinsic::getDeclaration(Sel.getModule(), Intrinsic::copysign, + Sel.getType()); + Instruction *CopySign = IntrinsicInst::Create(F, { MagArg, X }); + CopySign->setFastMathFlags(Sel.getFastMathFlags()); + return CopySign; +} + Instruction *InstCombiner::visitSelectInst(SelectInst &SI) { Value *CondVal = SI.getCondition(); Value *TrueVal = SI.getTrueValue(); @@ -2785,5 +2827,8 @@ if (Instruction *Rot = foldSelectRotate(SI)) return Rot; + if (Instruction *Copysign = foldSelectToCopysign(SI, Builder)) + return Copysign; + return nullptr; } diff --git a/llvm/test/Transforms/InstCombine/select.ll b/llvm/test/Transforms/InstCombine/select.ll --- a/llvm/test/Transforms/InstCombine/select.ll +++ b/llvm/test/Transforms/InstCombine/select.ll @@ -1535,9 +1535,7 @@ define float @copysign1(float %x) { ; CHECK-LABEL: @copysign1( -; CHECK-NEXT: [[I:%.*]] = bitcast float [[X:%.*]] to i32 -; CHECK-NEXT: [[ISPOS:%.*]] = icmp sgt i32 [[I]], -1 -; CHECK-NEXT: [[R:%.*]] = select i1 [[ISPOS]], float 1.000000e+00, float -1.000000e+00 +; CHECK-NEXT: [[R:%.*]] = call float @llvm.copysign.f32(float 1.000000e+00, float [[X:%.*]]) ; CHECK-NEXT: ret float [[R]] ; %i = bitcast float %x to i32 @@ -1548,9 +1546,8 @@ define <2 x float> @copysign2(<2 x float> %x) { ; CHECK-LABEL: @copysign2( -; CHECK-NEXT: [[I:%.*]] = bitcast <2 x float> [[X:%.*]] to <2 x i32> -; CHECK-NEXT: [[ISNEG:%.*]] = icmp slt <2 x i32> [[I]], zeroinitializer -; CHECK-NEXT: [[R:%.*]] = select nsz <2 x i1> [[ISNEG]], <2 x float> , <2 x float> +; CHECK-NEXT: [[TMP1:%.*]] = fneg nsz <2 x float> [[X:%.*]] +; CHECK-NEXT: [[R:%.*]] = call nsz <2 x float> @llvm.copysign.v2f32(<2 x float> , <2 x float> [[TMP1]]) ; CHECK-NEXT: ret <2 x float> [[R]] ; %i = bitcast <2 x float> %x to <2 x i32> @@ -1561,9 +1558,8 @@ define float @copysign3(float %x) { ; CHECK-LABEL: @copysign3( -; CHECK-NEXT: [[I:%.*]] = bitcast float [[X:%.*]] to i32 -; CHECK-NEXT: [[ISPOS:%.*]] = icmp sgt i32 [[I]], -1 -; CHECK-NEXT: [[R:%.*]] = select fast i1 [[ISPOS]], float -4.300000e+01, float 4.300000e+01 +; CHECK-NEXT: [[TMP1:%.*]] = fneg fast float [[X:%.*]] +; CHECK-NEXT: [[R:%.*]] = call fast float @llvm.copysign.f32(float 4.300000e+01, float [[TMP1]]) ; CHECK-NEXT: ret float [[R]] ; %i = bitcast float %x to i32 @@ -1572,6 +1568,8 @@ ret float %r } +; TODO: Allow undefs when matching vectors. + define <2 x float> @copysign4(<2 x float> %x) { ; CHECK-LABEL: @copysign4( ; CHECK-NEXT: [[I:%.*]] = bitcast <2 x float> [[X:%.*]] to <2 x i32> @@ -1587,6 +1585,8 @@ declare void @use1(i1) +; Negative test + define float @copysign_extra_use(float %x) { ; CHECK-LABEL: @copysign_extra_use( ; CHECK-NEXT: [[I:%.*]] = bitcast float [[X:%.*]] to i32 @@ -1602,6 +1602,8 @@ ret float %r } +; Negative test + define float @copysign_type_mismatch(double %x) { ; CHECK-LABEL: @copysign_type_mismatch( ; CHECK-NEXT: [[I:%.*]] = bitcast double [[X:%.*]] to i64 @@ -1615,6 +1617,8 @@ ret float %r } +; Negative test + define float @copysign_wrong_cmp(float %x) { ; CHECK-LABEL: @copysign_wrong_cmp( ; CHECK-NEXT: [[I:%.*]] = bitcast float [[X:%.*]] to i32 @@ -1628,6 +1632,8 @@ ret float %r } +; Negative test + define float @copysign_wrong_const(float %x) { ; CHECK-LABEL: @copysign_wrong_const( ; CHECK-NEXT: [[I:%.*]] = bitcast float [[X:%.*]] to i32