diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index e12a802e2e9ede..5c77b605d462a8 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -297,6 +297,12 @@ DWARF Support in Clang Floating Point Support in Clang ------------------------------- +Fixed Point Support in Clang +---------------------------- + +- Support fixed point precision macros according to ``7.18a.3`` of + `ISO/IEC TR 18037:2008 `_. + AST Matchers ------------ diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 877e205e2e9bfa..1b250cda42a4dd 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -768,6 +768,60 @@ void InitializeOpenCLFeatureTestMacros(const TargetInfo &TI, Builder.defineMacro("__opencl_c_int64"); } +llvm::SmallString<32> ConstructFixedPointLiteral(llvm::APFixedPoint Val, + llvm::StringRef Suffix) { + if (Val.isSigned() && Val == llvm::APFixedPoint::getMin(Val.getSemantics())) { + // When representing the min value of a signed fixed point type in source + // code, we cannot simply write `-`. For example, the min + // value of a `short _Fract` cannot be written as `-1.0hr`. This is because + // the parser will read this (and really any negative numerical literal) as + // a UnaryOperator that owns a FixedPointLiteral with a positive value + // rather than just a FixedPointLiteral with a negative value. Compiling + // `-1.0hr` results in an overflow to the maximal value of that fixed point + // type. The correct way to represent a signed min value is to instead split + // it into two halves, like `(-0.5hr-0.5hr)` which is what the standard + // defines SFRACT_MIN as. + llvm::SmallString<32> Literal; + Literal.push_back('('); + llvm::SmallString<32> HalfStr = + ConstructFixedPointLiteral(Val.shr(1), Suffix); + Literal += HalfStr; + Literal += HalfStr; + Literal.push_back(')'); + return Literal; + } + + llvm::SmallString<32> Str(Val.toString()); + Str += Suffix; + return Str; +} + +void DefineFixedPointMacros(const TargetInfo &TI, MacroBuilder &Builder, + llvm::StringRef TypeName, llvm::StringRef Suffix, + unsigned Width, unsigned Scale, bool Signed) { + // Saturation doesn't affect the size or scale of a fixed point type, so we + // don't need it here. + llvm::FixedPointSemantics FXSema( + Width, Scale, Signed, /*IsSaturated=*/false, + !Signed && TI.doUnsignedFixedPointTypesHavePadding()); + llvm::SmallString<32> MacroPrefix("__"); + MacroPrefix += TypeName; + Builder.defineMacro(MacroPrefix + "_EPSILON__", + ConstructFixedPointLiteral( + llvm::APFixedPoint::getEpsilon(FXSema), Suffix)); + Builder.defineMacro(MacroPrefix + "_FBIT__", Twine(Scale)); + Builder.defineMacro( + MacroPrefix + "_MAX__", + ConstructFixedPointLiteral(llvm::APFixedPoint::getMax(FXSema), Suffix)); + + // ISO/IEC TR 18037:2008 doesn't specify MIN macros for unsigned types since + // they're all just zero. + if (Signed) + Builder.defineMacro( + MacroPrefix + "_MIN__", + ConstructFixedPointLiteral(llvm::APFixedPoint::getMin(FXSema), Suffix)); +} + static void InitializePredefinedMacros(const TargetInfo &TI, const LangOptions &LangOpts, const FrontendOptions &FEOpts, @@ -1097,6 +1151,47 @@ static void InitializePredefinedMacros(const TargetInfo &TI, TI.getTypeWidth(TI.getIntMaxType()) && "uintmax_t and intmax_t have different widths?"); + if (LangOpts.FixedPoint) { + // Each unsigned type has the same width as their signed type. + DefineFixedPointMacros(TI, Builder, "SFRACT", "HR", TI.getShortFractWidth(), + TI.getShortFractScale(), /*Signed=*/true); + DefineFixedPointMacros(TI, Builder, "USFRACT", "UHR", + TI.getShortFractWidth(), + TI.getUnsignedShortFractScale(), /*Signed=*/false); + DefineFixedPointMacros(TI, Builder, "FRACT", "R", TI.getFractWidth(), + TI.getFractScale(), /*Signed=*/true); + DefineFixedPointMacros(TI, Builder, "UFRACT", "UR", TI.getFractWidth(), + TI.getUnsignedFractScale(), /*Signed=*/false); + DefineFixedPointMacros(TI, Builder, "LFRACT", "LR", TI.getLongFractWidth(), + TI.getLongFractScale(), /*Signed=*/true); + DefineFixedPointMacros(TI, Builder, "ULFRACT", "ULR", + TI.getLongFractWidth(), + TI.getUnsignedLongFractScale(), /*Signed=*/false); + DefineFixedPointMacros(TI, Builder, "SACCUM", "HK", TI.getShortAccumWidth(), + TI.getShortAccumScale(), /*Signed=*/true); + DefineFixedPointMacros(TI, Builder, "USACCUM", "UHK", + TI.getShortAccumWidth(), + TI.getUnsignedShortAccumScale(), /*Signed=*/false); + DefineFixedPointMacros(TI, Builder, "ACCUM", "K", TI.getAccumWidth(), + TI.getAccumScale(), /*Signed=*/true); + DefineFixedPointMacros(TI, Builder, "UACCUM", "UK", TI.getAccumWidth(), + TI.getUnsignedAccumScale(), /*Signed=*/false); + DefineFixedPointMacros(TI, Builder, "LACCUM", "LK", TI.getLongAccumWidth(), + TI.getLongAccumScale(), /*Signed=*/true); + DefineFixedPointMacros(TI, Builder, "ULACCUM", "ULK", + TI.getLongAccumWidth(), + TI.getUnsignedLongAccumScale(), /*Signed=*/false); + + Builder.defineMacro("__SACCUM_IBIT__", Twine(TI.getShortAccumIBits())); + Builder.defineMacro("__USACCUM_IBIT__", + Twine(TI.getUnsignedShortAccumIBits())); + Builder.defineMacro("__ACCUM_IBIT__", Twine(TI.getAccumIBits())); + Builder.defineMacro("__UACCUM_IBIT__", Twine(TI.getUnsignedAccumIBits())); + Builder.defineMacro("__LACCUM_IBIT__", Twine(TI.getLongAccumIBits())); + Builder.defineMacro("__ULACCUM_IBIT__", + Twine(TI.getUnsignedLongAccumIBits())); + } + if (TI.hasFloat16Type()) DefineFloatMacros(Builder, "FLT16", &TI.getHalfFormat(), "F16"); DefineFloatMacros(Builder, "FLT", &TI.getFloatFormat(), "F"); diff --git a/clang/test/Preprocessor/fixed-point.c b/clang/test/Preprocessor/fixed-point.c new file mode 100644 index 00000000000000..3adf36d3338649 --- /dev/null +++ b/clang/test/Preprocessor/fixed-point.c @@ -0,0 +1,67 @@ +/// Assert the fixed point precision macros according to ISO/IEC TR 18037:2008 7.18a.3 are +/// defined when -ffixed-point is provided. + +// RUN: %clang_cc1 -triple=x86_64 -E -dM -ffixed-point -x c < /dev/null | FileCheck -match-full-lines %s +// RUN: %clang_cc1 -triple=x86_64 -E -dM -ffixed-point -x c++ < /dev/null | FileCheck -match-full-lines %s + +/// These are the implementation-defined values for x86_64. +// CHECK-DAG:#define __SFRACT_EPSILON__ 0.0078125HR +// CHECK-DAG:#define __SFRACT_FBIT__ 7 +// CHECK-DAG:#define __SFRACT_MAX__ 0.9921875HR +// CHECK-DAG:#define __SFRACT_MIN__ (-0.5HR-0.5HR) + +// CHECK-DAG:#define __USFRACT_EPSILON__ 0.00390625UHR +// CHECK-DAG:#define __USFRACT_FBIT__ 8 +// CHECK-DAG:#define __USFRACT_MAX__ 0.99609375UHR + +// CHECK-DAG:#define __FRACT_EPSILON__ 0.000030517578125R +// CHECK-DAG:#define __FRACT_FBIT__ 15 +// CHECK-DAG:#define __FRACT_MAX__ 0.999969482421875R +// CHECK-DAG:#define __FRACT_MIN__ (-0.5R-0.5R) + +// CHECK-DAG:#define __UFRACT_EPSILON__ 0.0000152587890625UR +// CHECK-DAG:#define __UFRACT_FBIT__ 16 +// CHECK-DAG:#define __UFRACT_MAX__ 0.9999847412109375UR + +// CHECK-DAG:#define __LFRACT_EPSILON__ 0.0000000004656612873077392578125LR +// CHECK-DAG:#define __LFRACT_FBIT__ 31 +// CHECK-DAG:#define __LFRACT_MAX__ 0.9999999995343387126922607421875LR +// CHECK-DAG:#define __LFRACT_MIN__ (-0.5LR-0.5LR) + +// CHECK-DAG:#define __ULFRACT_EPSILON__ 0.00000000023283064365386962890625ULR +// CHECK-DAG:#define __ULFRACT_FBIT__ 32 +// CHECK-DAG:#define __ULFRACT_MAX__ 0.99999999976716935634613037109375ULR + +// CHECK-DAG:#define __SACCUM_EPSILON__ 0.0078125HK +// CHECK-DAG:#define __SACCUM_FBIT__ 7 +// CHECK-DAG:#define __SACCUM_MAX__ 255.9921875HK +// CHECK-DAG:#define __SACCUM_MIN__ (-128.0HK-128.0HK) + +// CHECK-DAG:#define __USACCUM_EPSILON__ 0.00390625UHK +// CHECK-DAG:#define __USACCUM_FBIT__ 8 +// CHECK-DAG:#define __USACCUM_MAX__ 255.99609375UHK + +// CHECK-DAG:#define __ACCUM_EPSILON__ 0.000030517578125K +// CHECK-DAG:#define __ACCUM_FBIT__ 15 +// CHECK-DAG:#define __ACCUM_MAX__ 65535.999969482421875K +// CHECK-DAG:#define __ACCUM_MIN__ (-32768.0K-32768.0K) + +// CHECK-DAG:#define __UACCUM_EPSILON__ 0.0000152587890625UK +// CHECK-DAG:#define __UACCUM_FBIT__ 16 +// CHECK-DAG:#define __UACCUM_MAX__ 65535.9999847412109375UK + +// CHECK-DAG:#define __LACCUM_EPSILON__ 0.0000000004656612873077392578125LK +// CHECK-DAG:#define __LACCUM_FBIT__ 31 +// CHECK-DAG:#define __LACCUM_MAX__ 4294967295.9999999995343387126922607421875LK +// CHECK-DAG:#define __LACCUM_MIN__ (-2147483648.0LK-2147483648.0LK) + +// CHECK-DAG:#define __ULACCUM_EPSILON__ 0.00000000023283064365386962890625ULK +// CHECK-DAG:#define __ULACCUM_FBIT__ 32 +// CHECK-DAG:#define __ULACCUM_MAX__ 4294967295.99999999976716935634613037109375ULK + +// CHECK-DAG:#define __SACCUM_IBIT__ 8 +// CHECK-DAG:#define __USACCUM_IBIT__ 8 +// CHECK-DAG:#define __ACCUM_IBIT__ 16 +// CHECK-DAG:#define __UACCUM_IBIT__ 16 +// CHECK-DAG:#define __LACCUM_IBIT__ 32 +// CHECK-DAG:#define __ULACCUM_IBIT__ 32 diff --git a/clang/test/Preprocessor/no-fixed-point.c b/clang/test/Preprocessor/no-fixed-point.c new file mode 100644 index 00000000000000..fe88ca22c9faa2 --- /dev/null +++ b/clang/test/Preprocessor/no-fixed-point.c @@ -0,0 +1,7 @@ +/// Assert the fixed point precision macros according to ISO/IEC TR 18037:2008 7.18a.3 are not +/// defined when -ffixed-point is not provided. + +// RUN: %clang_cc1 -triple=x86_64 -E -dM -x c < /dev/null | FileCheck -match-full-lines %s +// RUN: %clang_cc1 -triple=x86_64 -E -dM -x c++ < /dev/null | FileCheck -match-full-lines %s + +// CHECK-NOT:#define __SFRACT_FBIT__ 7 diff --git a/llvm/include/llvm/ADT/APFixedPoint.h b/llvm/include/llvm/ADT/APFixedPoint.h index b0c510865f444e..0c014e76aa7126 100644 --- a/llvm/include/llvm/ADT/APFixedPoint.h +++ b/llvm/include/llvm/ADT/APFixedPoint.h @@ -260,6 +260,7 @@ class APFixedPoint { static APFixedPoint getMax(const FixedPointSemantics &Sema); static APFixedPoint getMin(const FixedPointSemantics &Sema); + static APFixedPoint getEpsilon(const FixedPointSemantics &Sema); /// Given a floating point semantic, return the next floating point semantic /// with a larger exponent and larger or equal mantissa. diff --git a/llvm/lib/Support/APFixedPoint.cpp b/llvm/lib/Support/APFixedPoint.cpp index 3eea01bc98093a..249c4f1e2153da 100644 --- a/llvm/lib/Support/APFixedPoint.cpp +++ b/llvm/lib/Support/APFixedPoint.cpp @@ -129,6 +129,12 @@ APFixedPoint APFixedPoint::getMin(const FixedPointSemantics &Sema) { return APFixedPoint(Val, Sema); } +APFixedPoint APFixedPoint::getEpsilon(const FixedPointSemantics &Sema) { + APSInt Val(Sema.getWidth(), !Sema.isSigned()); + Val.setBit(/*BitPosition=*/0); + return APFixedPoint(Val, Sema); +} + bool FixedPointSemantics::fitsInFloatSemantics( const fltSemantics &FloatSema) const { // A fixed point semantic fits in a floating point semantic if the maximum