Skip to content

Commit

Permalink
[clang] Add fixed point precision macros (#81207)
Browse files Browse the repository at this point in the history
This defines the builtin macros specified in `7.18a.3 Precision macros`
of ISO/IEC TR 18037:2008. These are the `__*__` versions of them and the
formal definitions in stdfix.h can use them.
  • Loading branch information
PiJoules committed Feb 14, 2024
1 parent 0fc5786 commit ad49657
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 0 deletions.
6 changes: 6 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,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 <https://standards.iso.org/ittf/PubliclyAvailableStandards/c051126_ISO_IEC_TR_18037_2008.zip>`_.

AST Matchers
------------

Expand Down
95 changes: 95 additions & 0 deletions clang/lib/Frontend/InitPreprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 `-<lowest value>`. 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,
Expand Down Expand Up @@ -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");
Expand Down
67 changes: 67 additions & 0 deletions clang/test/Preprocessor/fixed-point.c
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions clang/test/Preprocessor/no-fixed-point.c
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions llvm/include/llvm/ADT/APFixedPoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Support/APFixedPoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit ad49657

Please sign in to comment.