Skip to content

Commit 6bff58b

Browse files
committed
[clang][DFP] Add __attribute__((mode(*D))) support for declaring decimal floating-point types.
Current proposals for supporting decimal floating-point (DFP) types in C++, e.g., ISO/IEC TR 24733:2011, do not specify keywords to be used as type specifiers. Rather, DFP types are specified as standard library types that, in practice, wrap a builtin type that is not otherwise exposed with a simple name. Gcc enables builtin DFP types to be declared using the GNU mode attribute as follows: float __attribute__((mode(SD))) // _Decimal32 float __attribute__((mode(DD))) // _Decimal64 float __attribute__((mode(TD))) // _Decimal128 This change implements support for these additional machine modes. This change also extends several of the clang::Type AST node attributes to include DFP types in addition to other floating-point types. These extensions match extensions to terms of the same name made in C23. The directly affected predicates includes: Type::isRealFloatingType() Type::isFloatingType() Type::isRealType() Type::isArithmeticType() The following new predicate can be used to differentiate DFP types where needed. Type::isDecimalFloatingType() The existing DFP specifications, including C23, do not include support for complex DFP types. Gcc does not provide such support either. These changes follow that existing precedent and do not enable support for complex DFP types as well. However, vector types are allowed to have DFP types as their element type.
1 parent f06fce6 commit 6bff58b

File tree

10 files changed

+169
-11
lines changed

10 files changed

+169
-11
lines changed

clang/include/clang/AST/Type.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2176,9 +2176,14 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
21762176
bool isUnscopedEnumerationType() const;
21772177

21782178
/// Floating point categories.
2179+
bool isDecimalFloatingType() const;
2180+
// C23 6.2.5p13 (_Decimal32/64/128)
21792181
bool isRealFloatingType() const; // C99 6.2.5p10 (float, double, long double)
2182+
// C23 6.2.5p14 (standard + decimal float)
2183+
// C23 H.2.4p5 (+interchange +extended FP)
21802184
/// isComplexType() does *not* include complex integers (a GCC extension).
21812185
/// isComplexIntegerType() can be used to test for complex integers.
2186+
/// C23 did not add complex decimal floating-point.
21822187
bool isComplexType() const; // C99 6.2.5p11 (complex)
21832188
bool isAnyComplexType() const; // C99 6.2.5p11 (complex) + Complex Int.
21842189
bool isFloatingType() const; // C99 6.2.5p11 (real floating + complex)
@@ -2746,8 +2751,12 @@ class BuiltinType : public Type {
27462751
return getKind() >= Bool && getKind() <= UInt128;
27472752
}
27482753

2754+
bool isDecimalFloatingPoint() const {
2755+
return getKind() >= DecimalFloat32 && getKind() <= DecimalFloat128;
2756+
}
2757+
27492758
bool isFloatingPoint() const {
2750-
return getKind() >= Half && getKind() <= Ibm128;
2759+
return getKind() >= Half && getKind() <= DecimalFloat128;
27512760
}
27522761

27532762
bool isSVEBool() const { return getKind() == Kind::SveBool; }

clang/include/clang/Basic/TargetInfo.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,23 @@ enum class FloatModeKind {
7575
LongDouble = 1 << 3,
7676
Float128 = 1 << 4,
7777
Ibm128 = 1 << 5,
78-
LLVM_MARK_AS_BITMASK_ENUM(Ibm128)
78+
Decimal32 = 1 << 6,
79+
Decimal64 = 1 << 7,
80+
Decimal128 = 1 << 8,
81+
LLVM_MARK_AS_BITMASK_ENUM(Decimal128)
7982
};
8083

84+
inline bool isDecimalFloatModeKind(FloatModeKind FMK) {
85+
switch (FMK) {
86+
case FloatModeKind::Decimal32:
87+
case FloatModeKind::Decimal64:
88+
case FloatModeKind::Decimal128:
89+
return true;
90+
default:
91+
return false;
92+
}
93+
}
94+
8195
/// Fields controlling how types are laid out in memory; these may need to
8296
/// be copied for targets like AMDGPU that base their ABIs on an auxiliary
8397
/// CPU target.

clang/lib/AST/ASTContext.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12164,6 +12164,12 @@ QualType ASTContext::getRealTypeForBitwidth(unsigned DestWidth,
1216412164
return Float128Ty;
1216512165
case FloatModeKind::Ibm128:
1216612166
return Ibm128Ty;
12167+
case FloatModeKind::Decimal32:
12168+
return DecimalFloat32Ty;
12169+
case FloatModeKind::Decimal64:
12170+
return DecimalFloat64Ty;
12171+
case FloatModeKind::Decimal128:
12172+
return DecimalFloat128Ty;
1216712173
case FloatModeKind::NoFloat:
1216812174
return {};
1216912175
}

clang/lib/AST/Type.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2190,7 +2190,7 @@ bool Type::hasUnsignedIntegerRepresentation() const {
21902190
bool Type::isFloatingType() const {
21912191
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
21922192
return BT->getKind() >= BuiltinType::Half &&
2193-
BT->getKind() <= BuiltinType::Ibm128;
2193+
BT->getKind() <= BuiltinType::DecimalFloat128;
21942194
if (const auto *CT = dyn_cast<ComplexType>(CanonicalType))
21952195
return CT->getElementType()->isFloatingType();
21962196
return false;
@@ -2204,6 +2204,12 @@ bool Type::hasFloatingRepresentation() const {
22042204
return isFloatingType();
22052205
}
22062206

2207+
bool Type::isDecimalFloatingType() const {
2208+
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
2209+
return BT->isDecimalFloatingPoint();
2210+
return false;
2211+
}
2212+
22072213
bool Type::isRealFloatingType() const {
22082214
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
22092215
return BT->isFloatingPoint();
@@ -2213,7 +2219,7 @@ bool Type::isRealFloatingType() const {
22132219
bool Type::isRealType() const {
22142220
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
22152221
return BT->getKind() >= BuiltinType::Bool &&
2216-
BT->getKind() <= BuiltinType::Ibm128;
2222+
BT->getKind() <= BuiltinType::DecimalFloat128;
22172223
if (const auto *ET = dyn_cast<EnumType>(CanonicalType))
22182224
return ET->getDecl()->isComplete() && !ET->getDecl()->isScoped();
22192225
return isBitIntType();
@@ -2222,7 +2228,7 @@ bool Type::isRealType() const {
22222228
bool Type::isArithmeticType() const {
22232229
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
22242230
return BT->getKind() >= BuiltinType::Bool &&
2225-
BT->getKind() <= BuiltinType::Ibm128;
2231+
BT->getKind() <= BuiltinType::DecimalFloat128;
22262232
if (const auto *ET = dyn_cast<EnumType>(CanonicalType))
22272233
// GCC allows forward declaration of enum types (forbid by C99 6.7.2.3p2).
22282234
// If a body isn't seen by the time we get here, return false.

clang/lib/Basic/TargetInfo.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,9 @@ TargetInfo::IntType TargetInfo::getLeastIntTypeByWidth(unsigned BitWidth,
319319

320320
FloatModeKind TargetInfo::getRealTypeByWidth(unsigned BitWidth,
321321
FloatModeKind ExplicitType) const {
322+
if (isDecimalFloatModeKind(ExplicitType))
323+
return ExplicitType;
324+
322325
if (getHalfWidth() == BitWidth)
323326
return FloatModeKind::Half;
324327
if (getFloatWidth() == BitWidth)

clang/lib/Sema/SemaChecking.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8531,15 +8531,20 @@ bool Sema::SemaBuiltinComplex(CallExpr *TheCall) {
85318531
<< Real->getSourceRange() << Imag->getSourceRange();
85328532
}
85338533

8534-
// We don't allow _Complex _Float16 nor _Complex __fp16 as type specifiers;
8535-
// don't allow this builtin to form those types either.
8534+
// We don't allow _Complex _Float16, _Complex __fp16, or _Complex _DecimalXX
8535+
// as type specifiers; don't allow this builtin to form those types either.
85368536
// FIXME: Should we allow these types?
85378537
if (Real->getType()->isFloat16Type())
85388538
return Diag(TheCall->getBeginLoc(), diag::err_invalid_complex_spec)
85398539
<< "_Float16";
85408540
if (Real->getType()->isHalfType())
85418541
return Diag(TheCall->getBeginLoc(), diag::err_invalid_complex_spec)
85428542
<< "half";
8543+
if (Real->getType()->isDecimalFloatingType()) {
8544+
const BuiltinType *BT = Real->getType()->getAs<BuiltinType>();
8545+
return Diag(TheCall->getBeginLoc(), diag::err_invalid_complex_spec)
8546+
<< BT->getName(Context.getPrintingPolicy());
8547+
}
85438548

85448549
TheCall->setType(Context.getComplexType(Real->getType()));
85458550
return false;

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4668,6 +4668,7 @@ static void parseModeAttrArg(Sema &S, StringRef Str, unsigned &DestWidth,
46684668
IntegerMode = true;
46694669
ComplexMode = false;
46704670
ExplicitType = FloatModeKind::NoFloat;
4671+
FloatModeKind ExplicitDFPType = FloatModeKind::NoFloat;
46714672
switch (Str.size()) {
46724673
case 2:
46734674
switch (Str[0]) {
@@ -4678,9 +4679,11 @@ static void parseModeAttrArg(Sema &S, StringRef Str, unsigned &DestWidth,
46784679
DestWidth = 16;
46794680
break;
46804681
case 'S':
4682+
ExplicitDFPType = FloatModeKind::Decimal32;
46814683
DestWidth = 32;
46824684
break;
46834685
case 'D':
4686+
ExplicitDFPType = FloatModeKind::Decimal64;
46844687
DestWidth = 64;
46854688
break;
46864689
case 'X':
@@ -4692,6 +4695,7 @@ static void parseModeAttrArg(Sema &S, StringRef Str, unsigned &DestWidth,
46924695
break;
46934696
case 'T':
46944697
ExplicitType = FloatModeKind::LongDouble;
4698+
ExplicitDFPType = FloatModeKind::Decimal128;
46954699
DestWidth = 128;
46964700
break;
46974701
case 'I':
@@ -4704,6 +4708,9 @@ static void parseModeAttrArg(Sema &S, StringRef Str, unsigned &DestWidth,
47044708
} else if (Str[1] == 'C') {
47054709
IntegerMode = false;
47064710
ComplexMode = true;
4711+
} else if (Str[1] == 'D') {
4712+
IntegerMode = false;
4713+
ExplicitType = ExplicitDFPType;
47074714
} else if (Str[1] != 'I') {
47084715
DestWidth = 0;
47094716
}
@@ -4853,6 +4860,13 @@ void Sema::AddModeAttr(Decl *D, const AttributeCommonInfo &CI,
48534860
return;
48544861
}
48554862

4863+
if (NewElemTy->isDecimalFloatingType()) {
4864+
if (!getLangOpts().DecimalFloatingPoint) {
4865+
Diag(AttrLoc, diag::err_dfp_disabled);
4866+
return;
4867+
}
4868+
}
4869+
48564870
if (ComplexMode) {
48574871
NewElemTy = Context.getComplexType(NewElemTy);
48584872
}

clang/test/Driver/dfp-enablement-lang.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,11 @@ _Decimal64 d64; // cxx-error {{unknown type name '_Decimal64'}} \
3434
// c-dfp-off-error {{decimal floating-point extensions are not enabled}}
3535
_Decimal128 d128; // cxx-error {{unknown type name '_Decimal128'}} \
3636
// c-dfp-off-error {{decimal floating-point extensions are not enabled}}
37+
38+
typedef float __attribute__((mode(SD))) D32; // dfp-off-error {{decimal floating-point extensions are not enabled}}
39+
typedef float __attribute__((mode(DD))) D64; // dfp-off-error {{decimal floating-point extensions are not enabled}}
40+
typedef float __attribute__((mode(TD))) D128; // dfp-off-error {{decimal floating-point extensions are not enabled}}
41+
42+
float __attribute__((mode(SD))) famsd; // dfp-off-error {{decimal floating-point extensions are not enabled}}
43+
float __attribute__((mode(DD))) famdd; // dfp-off-error {{decimal floating-point extensions are not enabled}}
44+
float __attribute__((mode(TD))) famtd; // dfp-off-error {{decimal floating-point extensions are not enabled}}

clang/test/Sema/dfp-types.c

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,88 @@
1-
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c23 -fexperimental-decimal-floating-point -fsyntax-only -verify=c %s
2-
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -x c++ -std=c++2c -fexperimental-decimal-floating-point -fsyntax-only -verify=cxx %s
3-
4-
// c-no-diagnostics
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c23 -fexperimental-decimal-floating-point -fsyntax-only -verify=expected,c %s
2+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -x c++ -std=c++2c -fexperimental-decimal-floating-point -fsyntax-only -verify=expected,cxx %s
53

64
// _Decimal32, _Decimal64, and _Decimal128 are never keywords in C++.
75
_Decimal32 d32; // cxx-error {{unknown type name '_Decimal32'}}
86
_Decimal64 d64; // cxx-error {{unknown type name '_Decimal64'}}
97
_Decimal128 d28; // cxx-error {{unknown type name '_Decimal128'}}
8+
9+
// DFP types are available via the GNU mode attribute in both C and C++.
10+
typedef float __attribute__((mode(SD))) D32;
11+
typedef float __attribute__((mode(DD))) D64;
12+
typedef float __attribute__((mode(TD))) D128;
13+
14+
// The GNU mode attribute requires a floating point base type for DFP types.
15+
// These are ok.
16+
long double __attribute((mode(SD))) ldamsd;
17+
double __attribute((mode(DD))) damdd;
18+
_Float16 __attribute((mode(SD))) f16amsd;
19+
__bf16 __attribute((mode(SD))) bf16amsd;
20+
__float128 __attribute((mode(TD))) f128amtd;
21+
// These are not ok.
22+
void __attribute((mode(SD))) vamsd; // expected-error {{type of machine mode does not match type of base type}}
23+
int __attribute((mode(DD))) iamdd; // expected-error {{type of machine mode does not match type of base type}}
24+
int* __attribute((mode(TD))) ipamtd; // expected-error {{mode attribute only supported for integer and floating-point types}}
25+
float __attribute((mode(TD))) *fapmtd; // expected-error {{mode attribute only supported for integer and floating-point types}}
26+
27+
// DFP types may be used as vector elements, but declaration form is restricted.
28+
float __attribute__((mode(V4SD))) famv4sd; // expected-warning {{deprecated; use the 'vector_size' attribute instead}}
29+
float __attribute__((mode(SD))) __attribute__((vector_size(16))) famsdv16;
30+
D64 __attribute__((vector_size(16))) d64av16;
31+
32+
// DFP types are not allowed as elements of complex types.
33+
D32 _Complex d32c; // expected-error {{'_Complex type-name' is invalid}}
34+
_Decimal32 _Complex kd32c; // c-error {{'_Complex _Decimal32' is invalid}} \
35+
cxx-error {{unknown type name '_Decimal32'}}
36+
37+
_Static_assert(sizeof(D32) == 4);
38+
_Static_assert(sizeof(D64) == 8);
39+
_Static_assert(sizeof(D128) == 16);
40+
41+
_Static_assert(_Alignof(D32) == 4);
42+
_Static_assert(_Alignof(D64) == 8);
43+
_Static_assert(_Alignof(D128) == 16);
44+
45+
struct s {
46+
D32 d32;
47+
D64 d64;
48+
D128 d128;
49+
union {
50+
D32 ud32;
51+
D64 ud64;
52+
D128 ud128;
53+
};
54+
};
55+
56+
struct bitfield {
57+
D32 d32 : 32; // expected-error {{bit-field 'd32' has non-integral type}}
58+
D64 d64 : 64; // expected-error {{bit-field 'd64' has non-integral type}}
59+
D128 d128 : 128; // expected-error {{bit-field 'd128' has non-integral type}}
60+
};
61+
62+
D32 test_d32(D32 d32) {
63+
return d32;
64+
}
65+
66+
D64 test_d64(D64 d64) {
67+
return d64;
68+
}
69+
70+
D128 test_d128(D128 d128) {
71+
return d128;
72+
}
73+
74+
void test_builtin_complex(D32 d32) {
75+
__auto_type lv = __builtin_complex(d32, d32); // expected-error {{'_Complex _Decimal32' is invalid}}
76+
}
77+
78+
void test_generic(D32 d32, D64 d64, D128 d128) {
79+
(void)_Generic(d32, D64 : 0, D128 : 0); // expected-error-re {{controlling expression type {{.*}} not compatible with any generic association type}}
80+
(void)_Generic(d64, D32 : 0, D128 : 0); // expected-error-re {{controlling expression type {{.*}} not compatible with any generic association type}}
81+
(void)_Generic(d128, D32 : 0, D64 : 0); // expected-error-re {{controlling expression type {{.*}} not compatible with any generic association type}}
82+
_Static_assert(_Generic(d32, D64 : 0, D128 : 0, default : 1) == 1);
83+
_Static_assert(_Generic(d64, D32 : 0, D128 : 0, default : 1) == 1);
84+
_Static_assert(_Generic(d128, D32 : 0, D64 : 0, default : 1) == 1);
85+
_Static_assert(_Generic(d32, D32 : 1, D64 : 0, D128 : 0) == 1);
86+
_Static_assert(_Generic(d64, D32 : 0, D64 : 1, D128 : 0) == 1);
87+
_Static_assert(_Generic(d128, D32 : 0, D64 : 0, D128 : 1) == 1);
88+
}

clang/test/SemaCXX/dfp-types.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++2c -fexperimental-decimal-floating-point -fsyntax-only -verify %s
2+
3+
using D32 = float __attribute__((mode(SD)));
4+
using D64 = float __attribute__((mode(DD)));
5+
using D128 = float __attribute__((mode(TD)));
6+
7+
// Dependent type specifiers for the GNU mode attribute base type are ok, but
8+
// must be of a valid type when instantiated.
9+
template<typename T>
10+
T __attribute((mode(SD))) dtamsd; // expected-error {{type of machine mode does not match type of base type}}
11+
auto g1 = dtamsd<float>;
12+
auto g2 = dtamsd<double>;
13+
auto g3 = dtamsd<long double>;
14+
auto g4 = dtamsd<int>; // expected-note {{in instantiation of variable template specialization 'dtamsd' requested here}}

0 commit comments

Comments
 (0)