Author: Serge Pavlov Date: 2024-05-17T12:06:34+07:00 New Revision: f4066fa2dd21c65bf0e24a479634c9a2d276cf8e
URL: https://github.com/llvm/llvm-project/commit/f4066fa2dd21c65bf0e24a479634c9a2d276cf8e DIFF: https://github.com/llvm/llvm-project/commit/f4066fa2dd21c65bf0e24a479634c9a2d276cf8e.diff LOG: [clang] Use constant rounding mode for floating literals (#90877) Conversion of floating-point literal to binary representation must be made using constant rounding mode, which can be changed using pragma FENV_ROUND. For example, the literal "0.1F" should be representes by either 0.099999994 or 0.100000001 depending on the rounding direction. Added: Modified: clang/include/clang/Lex/LiteralSupport.h clang/lib/Lex/LiteralSupport.cpp clang/lib/Sema/SemaExpr.cpp clang/test/AST/const-fpfeatures.c clang/test/AST/const-fpfeatures.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Lex/LiteralSupport.h b/clang/include/clang/Lex/LiteralSupport.h index 2ed42d1c5f9aa..705021fcfa5b1 100644 --- a/clang/include/clang/Lex/LiteralSupport.h +++ b/clang/include/clang/Lex/LiteralSupport.h @@ -118,12 +118,10 @@ class NumericLiteralParser { /// bits of the result and return true. Otherwise, return false. bool GetIntegerValue(llvm::APInt &Val); - /// GetFloatValue - Convert this numeric literal to a floating value, using - /// the specified APFloat fltSemantics (specifying float, double, etc). - /// The optional bool isExact (passed-by-reference) has its value - /// set to true if the returned APFloat can represent the number in the - /// literal exactly, and false otherwise. - llvm::APFloat::opStatus GetFloatValue(llvm::APFloat &Result); + /// Convert this numeric literal to a floating value, using the specified + /// APFloat fltSemantics (specifying float, double, etc) and rounding mode. + llvm::APFloat::opStatus GetFloatValue(llvm::APFloat &Result, + llvm::RoundingMode RM); /// GetFixedPointValue - Convert this numeric literal value into a /// scaled integer that represents this value. Returns true if an overflow diff --git a/clang/lib/Lex/LiteralSupport.cpp b/clang/lib/Lex/LiteralSupport.cpp index 9c0cbea5052cb..3df0391bdda77 100644 --- a/clang/lib/Lex/LiteralSupport.cpp +++ b/clang/lib/Lex/LiteralSupport.cpp @@ -1520,7 +1520,8 @@ bool NumericLiteralParser::GetIntegerValue(llvm::APInt &Val) { } llvm::APFloat::opStatus -NumericLiteralParser::GetFloatValue(llvm::APFloat &Result) { +NumericLiteralParser::GetFloatValue(llvm::APFloat &Result, + llvm::RoundingMode RM) { using llvm::APFloat; unsigned n = std::min(SuffixBegin - ThisTokBegin, ThisTokEnd - ThisTokBegin); @@ -1534,8 +1535,7 @@ NumericLiteralParser::GetFloatValue(llvm::APFloat &Result) { Str = Buffer; } - auto StatusOrErr = - Result.convertFromString(Str, APFloat::rmNearestTiesToEven); + auto StatusOrErr = Result.convertFromString(Str, RM); assert(StatusOrErr && "Invalid floating point representation"); return !errorToBool(StatusOrErr.takeError()) ? *StatusOrErr : APFloat::opInvalidOp; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 274e1fb183534..e0aae6333e1a1 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3699,7 +3699,10 @@ static Expr *BuildFloatingLiteral(Sema &S, NumericLiteralParser &Literal, using llvm::APFloat; APFloat Val(Format); - APFloat::opStatus result = Literal.GetFloatValue(Val); + llvm::RoundingMode RM = S.CurFPFeatures.getRoundingMode(); + if (RM == llvm::RoundingMode::Dynamic) + RM = llvm::RoundingMode::NearestTiesToEven; + APFloat::opStatus result = Literal.GetFloatValue(Val, RM); // Overflow is always an error, but underflow is only an error if // we underflowed to zero (APFloat reports denormals as underflow). diff --git a/clang/test/AST/const-fpfeatures.c b/clang/test/AST/const-fpfeatures.c index 083350fdc8ce6..8dc3221b0638a 100644 --- a/clang/test/AST/const-fpfeatures.c +++ b/clang/test/AST/const-fpfeatures.c @@ -19,6 +19,9 @@ float FI1u = 0xFFFFFFFFU; float _Complex C1u = C0; // CHECK: @C1u = {{.*}} { float, float } { float 0x3FF0000020000000, float 0x3FF0000020000000 } +float FLu = 0.1F; +// CHECK: @FLu = {{.*}} float 0x3FB99999A0000000 + #pragma STDC FENV_ROUND FE_DOWNWARD @@ -35,3 +38,6 @@ float FI1d = 0xFFFFFFFFU; float _Complex C1d = C0; // CHECK: @C1d = {{.*}} { float, float } { float 1.000000e+00, float 1.000000e+00 } + +float FLd = 0.1F; +// CHECK: @FLd = {{.*}} float 0x3FB9999980000000 diff --git a/clang/test/AST/const-fpfeatures.cpp b/clang/test/AST/const-fpfeatures.cpp index 95eb613df7f07..5e903c8c0e874 100644 --- a/clang/test/AST/const-fpfeatures.cpp +++ b/clang/test/AST/const-fpfeatures.cpp @@ -79,3 +79,108 @@ float V7 = []() -> float { 0x0.000001p0F); }(); // CHECK: @V7 = {{.*}} float 1.000000e+00 + +#pragma STDC FENV_ROUND FE_DYNAMIC + +template<float V> struct L { + constexpr L() : value(V) {} + float value; +}; + +#pragma STDC FENV_ROUND FE_DOWNWARD +L<0.1F> val_d; +// CHECK: @val_d = {{.*}} { float 0x3FB9999980000000 } + +#pragma STDC FENV_ROUND FE_UPWARD +L<0.1F> val_u; +// CHECK: @val_u = {{.*}} { float 0x3FB99999A0000000 } + + +// Check literals in macros. + +#pragma STDC FENV_ROUND FE_DOWNWARD +#define CONSTANT_0_1 0.1F + +#pragma STDC FENV_ROUND FE_UPWARD +float C1_ru = CONSTANT_0_1; +// CHECK: @C1_ru = {{.*}} float 0x3FB99999A0000000 + +#pragma STDC FENV_ROUND FE_DOWNWARD +float C1_rd = CONSTANT_0_1; +// CHECK: @C1_rd = {{.*}} float 0x3FB9999980000000 + +#pragma STDC FENV_ROUND FE_DOWNWARD +#define PRAGMA(x) _Pragma(#x) +#define CONSTANT_0_1_RM(v, rm) ([](){ PRAGMA(STDC FENV_ROUND rm); return v; }()) + +#pragma STDC FENV_ROUND FE_UPWARD +float C2_rd = CONSTANT_0_1_RM(0.1F, FE_DOWNWARD); +float C2_ru = CONSTANT_0_1_RM(0.1F, FE_UPWARD); +// CHECK: @C2_rd = {{.*}} float 0x3FB9999980000000 +// CHECK: @C2_ru = {{.*}} float 0x3FB99999A0000000 + +#pragma STDC FENV_ROUND FE_DOWNWARD +float C3_rd = CONSTANT_0_1_RM(0.1F, FE_DOWNWARD); +float C3_ru = CONSTANT_0_1_RM(0.1F, FE_UPWARD); +// CHECK: @C3_rd = {{.*}} float 0x3FB9999980000000 +// CHECK: @C3_ru = {{.*}} float 0x3FB99999A0000000 + +// Check literals in template instantiations. + +#pragma STDC FENV_ROUND FE_DYNAMIC + +template<typename T, T C> +constexpr T foo() { + return C; +} + +#pragma STDC FENV_ROUND FE_DOWNWARD +float var_d = foo<float, 0.1F>(); +// CHECK: @var_d = {{.*}} float 0x3FB9999980000000 + +#pragma STDC FENV_ROUND FE_UPWARD +float var_u = foo<float, 0.1F>(); +// CHECK: @var_u = {{.*}} float 0x3FB99999A0000000 + +#pragma STDC FENV_ROUND FE_DYNAMIC + +template<typename T, T f> void foo2() { + T Val = f; +} + +void func_01() { + #pragma STDC FENV_ROUND FE_DOWNWARD + foo2<float, 0.1f>(); +} + +void func_02() { + #pragma STDC FENV_ROUND FE_UPWARD + foo2<float, 0.1f>(); +} + +// CHECK-LABEL: define {{.*}} void @_Z4foo2IfTnT_Lf3dccccccEEvv() +// CHECK: store float 0x3FB9999980000000, ptr + +// CHECK-LABEL: define {{.*}} void @_Z4foo2IfTnT_Lf3dcccccdEEvv() +// CHECK: store float 0x3FB99999A0000000, ptr + + +#pragma STDC FENV_ROUND FE_DOWNWARD +template <int C> +float tfunc_01() { + return 0.1F; // Must be 0x3FB9999980000000 in all instantiations. +} +template float tfunc_01<0>(); +// CHECK-LABEL: define {{.*}} float @_Z8tfunc_01ILi0EEfv() +// CHECK: ret float 0x3FB9999980000000 + +#pragma STDC FENV_ROUND FE_UPWARD +template float tfunc_01<1>(); +// CHECK-LABEL: define {{.*}} float @_Z8tfunc_01ILi1EEfv() +// CHECK: ret float 0x3FB9999980000000 + +template<> float tfunc_01<2>() { + return 0.1F; +} +// CHECK-LABEL: define {{.*}} float @_Z8tfunc_01ILi2EEfv() +// CHECK: ret float 0x3FB99999A0000000 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits