diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp --- a/llvm/lib/Support/APFloat.cpp +++ b/llvm/lib/Support/APFloat.cpp @@ -2226,9 +2226,16 @@ } // If this is a truncation, perform the shift before we narrow the storage. - if (shift < 0 && (isFiniteNonZero() || category==fcNaN)) + if (shift < 0 && (isFiniteNonZero() || category==fcNaN)) { lostFraction = shiftRight(significandParts(), oldPartCount, -shift); + // If we are truncating a denormal, the lostFraction is not relevant when + // normalizing, but it can't be lfExactlyZero or lfMoreThanHalf, otherwise + // it would return incorrect result if the significand is zero after shift + if (isDenormal()) + lostFraction = lfLessThanHalf; + } + // Fix the storage so it can hold to new value. if (newPartCount > oldPartCount) { // The new type requires more storage; make it available. diff --git a/llvm/test/Transforms/InstSimplify/ConstProp/cast.ll b/llvm/test/Transforms/InstSimplify/ConstProp/cast.ll --- a/llvm/test/Transforms/InstSimplify/ConstProp/cast.ll +++ b/llvm/test/Transforms/InstSimplify/ConstProp/cast.ll @@ -79,21 +79,17 @@ ret float %b } -; FIXME: This should be 0.0. - define float @trunc_denorm_lost_fraction1() { ; CHECK-LABEL: @trunc_denorm_lost_fraction1( -; CHECK-NEXT: ret float 0x36A0000000000000 +; CHECK-NEXT: ret float 0.000000e+00 ; %b = fptrunc double 0x0000000010000001 to float ret float %b } -; FIXME: This should be 0.0. - define float @trunc_denorm_lost_fraction2() { ; CHECK-LABEL: @trunc_denorm_lost_fraction2( -; CHECK-NEXT: ret float 0x36A0000000000000 +; CHECK-NEXT: ret float 0.000000e+00 ; %b = fptrunc double 0x000000001fffffff to float ret float %b @@ -107,11 +103,9 @@ ret float %b } -; FIXME: This should be -0.0. - define float @trunc_denorm_lost_fraction4() { ; CHECK-LABEL: @trunc_denorm_lost_fraction4( -; CHECK-NEXT: ret float 0xB6A0000000000000 +; CHECK-NEXT: ret float -0.000000e+00 ; %b = fptrunc double 0x8000000010000001 to float ret float %b diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp --- a/llvm/unittests/ADT/APFloatTest.cpp +++ b/llvm/unittests/ADT/APFloatTest.cpp @@ -1859,6 +1859,32 @@ EXPECT_EQ(0x7fc00000, test.bitcastToAPInt()); EXPECT_TRUE(losesInfo); EXPECT_EQ(status, APFloat::opOK); + + // Test that subnormals are handled correctly in double to float conversion + test = APFloat(APFloat::IEEEdouble(), "0x0.0000010000000p-1022"); + test.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo); + EXPECT_EQ(0.0f, test.convertToFloat()); + EXPECT_TRUE(losesInfo); + + test = APFloat(APFloat::IEEEdouble(), "0x0.0000010000001p-1022"); + test.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo); + EXPECT_EQ(0.0f, test.convertToFloat()); + EXPECT_TRUE(losesInfo); + + test = APFloat(APFloat::IEEEdouble(), "-0x0.0000010000001p-1022"); + test.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo); + EXPECT_EQ(0.0f, test.convertToFloat()); + EXPECT_TRUE(losesInfo); + + test = APFloat(APFloat::IEEEdouble(), "0x0.0000020000000p-1022"); + test.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo); + EXPECT_EQ(0.0f, test.convertToFloat()); + EXPECT_TRUE(losesInfo); + + test = APFloat(APFloat::IEEEdouble(), "0x0.0000020000001p-1022"); + test.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo); + EXPECT_EQ(0.0f, test.convertToFloat()); + EXPECT_TRUE(losesInfo); } TEST(APFloatTest, PPCDoubleDouble) {