Index: llvm/include/llvm/Analysis/ValueTracking.h =================================================================== --- llvm/include/llvm/Analysis/ValueTracking.h +++ llvm/include/llvm/Analysis/ValueTracking.h @@ -384,15 +384,25 @@ } void copysign(const KnownFPClass &Sign) { - // Start assuming nothing about the sign. + // Don't know anything about the sign of the source. Expand the possible set + // to its opposite sign pair. + if (KnownFPClasses & fcZero) + KnownFPClasses |= fcZero; + if (KnownFPClasses & fcSubnormal) + KnownFPClasses |= fcSubnormal; + if (KnownFPClasses & fcNormal) + KnownFPClasses |= fcNormal; + if (KnownFPClasses & fcInf) + KnownFPClasses |= fcInf; + + // Sign bit is exactly preserved even for nans. SignBit = Sign.SignBit; - if (!SignBit) - return; - if (*SignBit) - KnownFPClasses = KnownFPClasses & fcNegative; - else - KnownFPClasses = KnownFPClasses & fcPositive; + // Clear sign bits based on the input sign mask. + if (Sign.isKnownNever(fcPositive | fcNan) || (SignBit && *SignBit)) + KnownFPClasses &= (fcNegative | fcNan); + if (Sign.isKnownNever(fcNegative | fcNan) || (SignBit && !*SignBit)) + KnownFPClasses &= (fcPositive | fcNan); } // Propagate knowledge that a non-NaN source implies the result can also not Index: llvm/test/Transforms/Attributor/nofpclass-copysign.ll =================================================================== --- llvm/test/Transforms/Attributor/nofpclass-copysign.ll +++ llvm/test/Transforms/Attributor/nofpclass-copysign.ll @@ -15,10 +15,10 @@ } define float @ret_copysign_fabs_rhs(float %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(nan ninf nzero nsub nnorm) float @ret_copysign_fabs_rhs +; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_copysign_fabs_rhs ; CHECK-SAME: (float [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[FABS_ARG1:%.*]] = call float @llvm.fabs.f32(float [[ARG1]]) #[[ATTR2]] -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan ninf nzero nsub nnorm) float @llvm.copysign.f32(float [[ARG0]], float [[FABS_ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(ninf nzero nsub nnorm) float @llvm.copysign.f32(float [[ARG0]], float [[FABS_ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %fabs.arg1 = call float @llvm.fabs.f32(float %arg1) @@ -40,10 +40,10 @@ define float @ret_copysign_lhs_fabs_nnan_rhs(float %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(nan ninf nzero nsub nnorm) float @ret_copysign_lhs_fabs_nnan_rhs +; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_copysign_lhs_fabs_nnan_rhs ; CHECK-SAME: (float [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[FABS_ARG1:%.*]] = call nnan float @llvm.fabs.f32(float [[ARG1]]) #[[ATTR2]] -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan ninf nzero nsub nnorm) float @llvm.copysign.f32(float [[ARG0]], float [[FABS_ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(ninf nzero nsub nnorm) float @llvm.copysign.f32(float [[ARG0]], float [[FABS_ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %fabs.arg1 = call nnan float @llvm.fabs.f32(float %arg1) @@ -53,9 +53,9 @@ define float @ret_copysign_noneg_lhs(float nofpclass(ninf nnorm nsub nzero) %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_copysign_noneg_lhs +; CHECK-LABEL: define float @ret_copysign_noneg_lhs ; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(ninf nzero nsub nnorm) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.copysign.f32(float %arg0, float %arg1) @@ -72,10 +72,20 @@ ret float %call } +define float @ret_copysign_noneg_nonan_rhs(float %arg0, float nofpclass(ninf nnorm nsub nzero nan) %arg1) { +; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_copysign_noneg_nonan_rhs +; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(nan ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(ninf nzero nsub nnorm) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: ret float [[CALL]] +; + %call = call float @llvm.copysign.f32(float %arg0, float %arg1) + ret float %call +} + define float @ret_copysign_nopos_lhs(float nofpclass(pinf pnorm psub pzero) %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_copysign_nopos_lhs +; CHECK-LABEL: define float @ret_copysign_nopos_lhs ; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(pinf pzero psub pnorm) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.copysign.f32(float %arg0, float %arg1) @@ -113,9 +123,9 @@ } define float @ret_copysign_noneg_nonan_lhs(float nofpclass(ninf nnorm nsub nzero nan) %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(nan ninf nzero nsub nnorm) float @ret_copysign_noneg_nonan_lhs +; CHECK-LABEL: define nofpclass(nan) float @ret_copysign_noneg_nonan_lhs ; CHECK-SAME: (float nofpclass(nan ninf nzero nsub nnorm) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan ninf nzero nsub nnorm) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.copysign.f32(float %arg0, float %arg1) @@ -123,9 +133,9 @@ } define float @ret_copysign_nopos_nonan_lhs(float nofpclass(pinf pnorm psub pzero nan) %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(nan pinf pzero psub pnorm) float @ret_copysign_nopos_nonan_lhs +; CHECK-LABEL: define nofpclass(nan) float @ret_copysign_nopos_nonan_lhs ; CHECK-SAME: (float nofpclass(nan pinf pzero psub pnorm) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan pinf pzero psub pnorm) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.copysign.f32(float %arg0, float %arg1) @@ -133,9 +143,9 @@ } define float @ret_copysign_mixed_lhs0(float nofpclass(ninf pnorm nsub nzero) %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(ninf nzero nsub pnorm) float @ret_copysign_mixed_lhs0 +; CHECK-LABEL: define float @ret_copysign_mixed_lhs0 ; CHECK-SAME: (float nofpclass(ninf nzero nsub pnorm) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(ninf nzero nsub pnorm) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.copysign.f32(float %arg0, float %arg1) @@ -143,9 +153,9 @@ } define float @ret_copysign_mixed_lhs1(float nofpclass(pinf nnorm psub pzero) %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(pinf pzero psub nnorm) float @ret_copysign_mixed_lhs1 +; CHECK-LABEL: define float @ret_copysign_mixed_lhs1 ; CHECK-SAME: (float nofpclass(pinf pzero psub nnorm) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(pinf pzero psub nnorm) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.copysign.f32(float %arg0, float %arg1) @@ -153,9 +163,9 @@ } define float @ret_copysign_mixed_lhs2(float nofpclass(ninf pnorm psub nzero qnan) %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(qnan ninf nzero psub pnorm) float @ret_copysign_mixed_lhs2 +; CHECK-LABEL: define nofpclass(qnan) float @ret_copysign_mixed_lhs2 ; CHECK-SAME: (float nofpclass(qnan ninf nzero psub pnorm) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(qnan ninf nzero psub pnorm) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(qnan) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.copysign.f32(float %arg0, float %arg1) @@ -163,9 +173,9 @@ } define float @ret_copysign_noninf_lhs(float nofpclass(ninf) %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(ninf) float @ret_copysign_noninf_lhs +; CHECK-LABEL: define float @ret_copysign_noninf_lhs ; CHECK-SAME: (float nofpclass(ninf) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(ninf) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.copysign.f32(float %arg0, float %arg1) @@ -173,9 +183,9 @@ } define float @ret_copysign_nopinf_lhs(float nofpclass(pinf) %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(pinf) float @ret_copysign_nopinf_lhs +; CHECK-LABEL: define float @ret_copysign_nopinf_lhs ; CHECK-SAME: (float nofpclass(pinf) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(pinf) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.copysign.f32(float %arg0, float %arg1) @@ -193,9 +203,9 @@ } define float @ret_copysign_nonzero_lhs(float nofpclass(nzero) %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(nzero) float @ret_copysign_nonzero_lhs +; CHECK-LABEL: define float @ret_copysign_nonzero_lhs ; CHECK-SAME: (float nofpclass(nzero) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nzero) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.copysign.f32(float %arg0, float %arg1) @@ -203,9 +213,9 @@ } define float @ret_copysign_nopzero_lhs(float nofpclass(pzero) %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(pzero) float @ret_copysign_nopzero_lhs +; CHECK-LABEL: define float @ret_copysign_nopzero_lhs ; CHECK-SAME: (float nofpclass(pzero) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(pzero) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.copysign.f32(float %arg0, float %arg1) @@ -223,9 +233,9 @@ } define float @ret_copysign_nonsub_lhs(float nofpclass(nsub) %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(nsub) float @ret_copysign_nonsub_lhs +; CHECK-LABEL: define float @ret_copysign_nonsub_lhs ; CHECK-SAME: (float nofpclass(nsub) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nsub) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.copysign.f32(float %arg0, float %arg1) @@ -233,9 +243,9 @@ } define float @ret_copysign_nopsub_lhs(float nofpclass(psub) %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(psub) float @ret_copysign_nopsub_lhs +; CHECK-LABEL: define float @ret_copysign_nopsub_lhs ; CHECK-SAME: (float nofpclass(psub) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(psub) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.copysign.f32(float %arg0, float %arg1) @@ -253,9 +263,9 @@ } define float @ret_copysign_nonnorm_lhs(float nofpclass(nnorm) %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(nnorm) float @ret_copysign_nonnorm_lhs +; CHECK-LABEL: define float @ret_copysign_nonnorm_lhs ; CHECK-SAME: (float nofpclass(nnorm) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nnorm) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.copysign.f32(float %arg0, float %arg1) @@ -263,9 +273,9 @@ } define float @ret_copysign_nopnorm_lhs(float nofpclass(pnorm) %arg0, float %arg1) { -; CHECK-LABEL: define nofpclass(pnorm) float @ret_copysign_nopnorm_lhs +; CHECK-LABEL: define float @ret_copysign_nopnorm_lhs ; CHECK-SAME: (float nofpclass(pnorm) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(pnorm) float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.copysign.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.copysign.f32(float %arg0, float %arg1) Index: llvm/unittests/Analysis/ValueTrackingTest.cpp =================================================================== --- llvm/unittests/Analysis/ValueTrackingTest.cpp +++ llvm/unittests/Analysis/ValueTrackingTest.cpp @@ -1493,7 +1493,7 @@ " %A = call float @llvm.copysign.f32(float %fabs, float %arg1)" " ret float %A\n" "}\n"); - expectKnownFPClass(fcPositive, std::nullopt); + expectKnownFPClass(~fcNan, std::nullopt); } TEST_F(ComputeKnownFPClassTest, CopySignNInfSrc0_NegSign) { @@ -1505,7 +1505,7 @@ " %A = call float @llvm.copysign.f32(float %ninf, float -1.0)" " ret float %A\n" "}\n"); - expectKnownFPClass(fcNegFinite, true); + expectKnownFPClass(fcNegFinite | fcNan, true); } TEST_F(ComputeKnownFPClassTest, CopySignNInfSrc0_PosSign) { @@ -1517,7 +1517,7 @@ " %A = call float @llvm.copysign.f32(float %ninf, float 1.0)" " ret float %A\n" "}\n"); - expectKnownFPClass(fcPosFinite, false); + expectKnownFPClass(fcPosFinite | fcNan, false); } TEST_F(ComputeKnownFPClassTest, UIToFP) {