diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp @@ -521,6 +521,13 @@ if (match(Op1, m_SpecificFP(-1.0))) return UnaryOperator::CreateFNegFMF(Op0, &I); + // With no-nans: X * 0.0 --> copysign(0.0, X) + if (I.hasNoNaNs() && match(Op1, m_PosZeroFP())) { + CallInst *CopySign = Builder.CreateIntrinsic(Intrinsic::copysign, + {I.getType()}, {Op1, Op0}, &I); + return replaceInstUsesWith(I, CopySign); + } + // -X * C --> X * -C Value *X, *Y; Constant *C; diff --git a/llvm/test/Transforms/InstCombine/fmul.ll b/llvm/test/Transforms/InstCombine/fmul.ll --- a/llvm/test/Transforms/InstCombine/fmul.ll +++ b/llvm/test/Transforms/InstCombine/fmul.ll @@ -1203,22 +1203,26 @@ define half @mul_zero_nnan(half %x) { ; CHECK-LABEL: @mul_zero_nnan( -; CHECK-NEXT: [[R:%.*]] = fmul nnan half [[X:%.*]], 0xH0000 -; CHECK-NEXT: ret half [[R]] +; CHECK-NEXT: [[TMP1:%.*]] = call nnan half @llvm.copysign.f16(half 0xH0000, half [[X:%.*]]) +; CHECK-NEXT: ret half [[TMP1]] ; %r = fmul nnan half %x, 0.0 ret half %r } +; poison propagates through vector elements + define <2 x float> @mul_zero_nnan_vec_poison(<2 x float> %x) { ; CHECK-LABEL: @mul_zero_nnan_vec_poison( -; CHECK-NEXT: [[R:%.*]] = fmul nnan <2 x float> [[X:%.*]], -; CHECK-NEXT: ret <2 x float> [[R]] +; CHECK-NEXT: [[TMP1:%.*]] = call nnan <2 x float> @llvm.copysign.v2f32(<2 x float> , <2 x float> [[X:%.*]]) +; CHECK-NEXT: ret <2 x float> [[TMP1]] ; %r = fmul nnan <2 x float> %x, ret <2 x float> %r } +; negative test - must have nnan + define half @mul_zero(half %x) { ; CHECK-LABEL: @mul_zero( ; CHECK-NEXT: [[R:%.*]] = fmul ninf nsz half [[X:%.*]], 0xH0000 @@ -1228,6 +1232,8 @@ ret half %r } +; TODO: This could be fneg+copysign. + define half @mul_negzero_nnan(half %x) { ; CHECK-LABEL: @mul_negzero_nnan( ; CHECK-NEXT: [[R:%.*]] = fmul nnan half [[X:%.*]], 0xH8000