Skip to content

[clang] Add fixed point precision macros #81207

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <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