diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 46dbadd8b878b..3fcec7b594abc 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2176,9 +2176,14 @@ class alignas(8) Type : public ExtQualsTypeCommonBase { bool isUnscopedEnumerationType() const; /// Floating point categories. + bool isDecimalFloatingType() const; + // C23 6.2.5p13 (_Decimal32/64/128) bool isRealFloatingType() const; // C99 6.2.5p10 (float, double, long double) + // C23 6.2.5p14 (standard + decimal float) + // C23 H.2.4p5 (+interchange +extended FP) /// isComplexType() does *not* include complex integers (a GCC extension). /// isComplexIntegerType() can be used to test for complex integers. + /// C23 did not add complex decimal floating-point. bool isComplexType() const; // C99 6.2.5p11 (complex) bool isAnyComplexType() const; // C99 6.2.5p11 (complex) + Complex Int. bool isFloatingType() const; // C99 6.2.5p11 (real floating + complex) @@ -2746,8 +2751,12 @@ class BuiltinType : public Type { return getKind() >= Bool && getKind() <= UInt128; } + bool isDecimalFloatingPoint() const { + return getKind() >= DecimalFloat32 && getKind() <= DecimalFloat128; + } + bool isFloatingPoint() const { - return getKind() >= Half && getKind() <= Ibm128; + return getKind() >= Half && getKind() <= DecimalFloat128; } bool isSVEBool() const { return getKind() == Kind::SveBool; } diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index ddf166c971fec..f8734dcda10c7 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -75,9 +75,23 @@ enum class FloatModeKind { LongDouble = 1 << 3, Float128 = 1 << 4, Ibm128 = 1 << 5, - LLVM_MARK_AS_BITMASK_ENUM(Ibm128) + Decimal32 = 1 << 6, + Decimal64 = 1 << 7, + Decimal128 = 1 << 8, + LLVM_MARK_AS_BITMASK_ENUM(Decimal128) }; +inline bool isDecimalFloatModeKind(FloatModeKind FMK) { + switch (FMK) { + case FloatModeKind::Decimal32: + case FloatModeKind::Decimal64: + case FloatModeKind::Decimal128: + return true; + default: + return false; + } +} + /// Fields controlling how types are laid out in memory; these may need to /// be copied for targets like AMDGPU that base their ABIs on an auxiliary /// CPU target. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 1a411232cab9d..0aef78b685460 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -12164,6 +12164,12 @@ QualType ASTContext::getRealTypeForBitwidth(unsigned DestWidth, return Float128Ty; case FloatModeKind::Ibm128: return Ibm128Ty; + case FloatModeKind::Decimal32: + return DecimalFloat32Ty; + case FloatModeKind::Decimal64: + return DecimalFloat64Ty; + case FloatModeKind::Decimal128: + return DecimalFloat128Ty; case FloatModeKind::NoFloat: return {}; } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 91de51add303e..26d813b160bc3 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2190,7 +2190,7 @@ bool Type::hasUnsignedIntegerRepresentation() const { bool Type::isFloatingType() const { if (const auto *BT = dyn_cast(CanonicalType)) return BT->getKind() >= BuiltinType::Half && - BT->getKind() <= BuiltinType::Ibm128; + BT->getKind() <= BuiltinType::DecimalFloat128; if (const auto *CT = dyn_cast(CanonicalType)) return CT->getElementType()->isFloatingType(); return false; @@ -2204,6 +2204,12 @@ bool Type::hasFloatingRepresentation() const { return isFloatingType(); } +bool Type::isDecimalFloatingType() const { + if (const auto *BT = dyn_cast(CanonicalType)) + return BT->isDecimalFloatingPoint(); + return false; +} + bool Type::isRealFloatingType() const { if (const auto *BT = dyn_cast(CanonicalType)) return BT->isFloatingPoint(); @@ -2213,7 +2219,7 @@ bool Type::isRealFloatingType() const { bool Type::isRealType() const { if (const auto *BT = dyn_cast(CanonicalType)) return BT->getKind() >= BuiltinType::Bool && - BT->getKind() <= BuiltinType::Ibm128; + BT->getKind() <= BuiltinType::DecimalFloat128; if (const auto *ET = dyn_cast(CanonicalType)) return ET->getDecl()->isComplete() && !ET->getDecl()->isScoped(); return isBitIntType(); @@ -2222,7 +2228,7 @@ bool Type::isRealType() const { bool Type::isArithmeticType() const { if (const auto *BT = dyn_cast(CanonicalType)) return BT->getKind() >= BuiltinType::Bool && - BT->getKind() <= BuiltinType::Ibm128; + BT->getKind() <= BuiltinType::DecimalFloat128; if (const auto *ET = dyn_cast(CanonicalType)) // GCC allows forward declaration of enum types (forbid by C99 6.7.2.3p2). // If a body isn't seen by the time we get here, return false. diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 5cd7e259db90d..6b34c9e8d33eb 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -319,6 +319,9 @@ TargetInfo::IntType TargetInfo::getLeastIntTypeByWidth(unsigned BitWidth, FloatModeKind TargetInfo::getRealTypeByWidth(unsigned BitWidth, FloatModeKind ExplicitType) const { + if (isDecimalFloatModeKind(ExplicitType)) + return ExplicitType; + if (getHalfWidth() == BitWidth) return FloatModeKind::Half; if (getFloatWidth() == BitWidth) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index fad70223362ed..97f175f770ca3 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -8531,8 +8531,8 @@ bool Sema::SemaBuiltinComplex(CallExpr *TheCall) { << Real->getSourceRange() << Imag->getSourceRange(); } - // We don't allow _Complex _Float16 nor _Complex __fp16 as type specifiers; - // don't allow this builtin to form those types either. + // We don't allow _Complex _Float16, _Complex __fp16, or _Complex _DecimalXX + // as type specifiers; don't allow this builtin to form those types either. // FIXME: Should we allow these types? if (Real->getType()->isFloat16Type()) return Diag(TheCall->getBeginLoc(), diag::err_invalid_complex_spec) @@ -8540,6 +8540,11 @@ bool Sema::SemaBuiltinComplex(CallExpr *TheCall) { if (Real->getType()->isHalfType()) return Diag(TheCall->getBeginLoc(), diag::err_invalid_complex_spec) << "half"; + if (Real->getType()->isDecimalFloatingType()) { + const BuiltinType *BT = Real->getType()->getAs(); + return Diag(TheCall->getBeginLoc(), diag::err_invalid_complex_spec) + << BT->getName(Context.getPrintingPolicy()); + } TheCall->setType(Context.getComplexType(Real->getType())); return false; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index cc98713241395..cf9c51329577e 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4668,6 +4668,7 @@ static void parseModeAttrArg(Sema &S, StringRef Str, unsigned &DestWidth, IntegerMode = true; ComplexMode = false; ExplicitType = FloatModeKind::NoFloat; + FloatModeKind ExplicitDFPType = FloatModeKind::NoFloat; switch (Str.size()) { case 2: switch (Str[0]) { @@ -4678,9 +4679,11 @@ static void parseModeAttrArg(Sema &S, StringRef Str, unsigned &DestWidth, DestWidth = 16; break; case 'S': + ExplicitDFPType = FloatModeKind::Decimal32; DestWidth = 32; break; case 'D': + ExplicitDFPType = FloatModeKind::Decimal64; DestWidth = 64; break; case 'X': @@ -4692,6 +4695,7 @@ static void parseModeAttrArg(Sema &S, StringRef Str, unsigned &DestWidth, break; case 'T': ExplicitType = FloatModeKind::LongDouble; + ExplicitDFPType = FloatModeKind::Decimal128; DestWidth = 128; break; case 'I': @@ -4704,6 +4708,9 @@ static void parseModeAttrArg(Sema &S, StringRef Str, unsigned &DestWidth, } else if (Str[1] == 'C') { IntegerMode = false; ComplexMode = true; + } else if (Str[1] == 'D') { + IntegerMode = false; + ExplicitType = ExplicitDFPType; } else if (Str[1] != 'I') { DestWidth = 0; } @@ -4853,6 +4860,13 @@ void Sema::AddModeAttr(Decl *D, const AttributeCommonInfo &CI, return; } + if (NewElemTy->isDecimalFloatingType()) { + if (!getLangOpts().DecimalFloatingPoint) { + Diag(AttrLoc, diag::err_dfp_disabled); + return; + } + } + if (ComplexMode) { NewElemTy = Context.getComplexType(NewElemTy); } diff --git a/clang/test/Driver/dfp-enablement-lang.c b/clang/test/Driver/dfp-enablement-lang.c index fe566e6cef848..05406d653a69f 100644 --- a/clang/test/Driver/dfp-enablement-lang.c +++ b/clang/test/Driver/dfp-enablement-lang.c @@ -34,3 +34,11 @@ _Decimal64 d64; // cxx-error {{unknown type name '_Decimal64'}} \ // c-dfp-off-error {{decimal floating-point extensions are not enabled}} _Decimal128 d128; // cxx-error {{unknown type name '_Decimal128'}} \ // c-dfp-off-error {{decimal floating-point extensions are not enabled}} + +typedef float __attribute__((mode(SD))) D32; // dfp-off-error {{decimal floating-point extensions are not enabled}} +typedef float __attribute__((mode(DD))) D64; // dfp-off-error {{decimal floating-point extensions are not enabled}} +typedef float __attribute__((mode(TD))) D128; // dfp-off-error {{decimal floating-point extensions are not enabled}} + +float __attribute__((mode(SD))) famsd; // dfp-off-error {{decimal floating-point extensions are not enabled}} +float __attribute__((mode(DD))) famdd; // dfp-off-error {{decimal floating-point extensions are not enabled}} +float __attribute__((mode(TD))) famtd; // dfp-off-error {{decimal floating-point extensions are not enabled}} diff --git a/clang/test/Sema/dfp-types.c b/clang/test/Sema/dfp-types.c index 86bb5ce945ca1..330fb898f9414 100644 --- a/clang/test/Sema/dfp-types.c +++ b/clang/test/Sema/dfp-types.c @@ -1,9 +1,88 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c23 -fexperimental-decimal-floating-point -fsyntax-only -verify=c %s -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -x c++ -std=c++2c -fexperimental-decimal-floating-point -fsyntax-only -verify=cxx %s - -// c-no-diagnostics +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c23 -fexperimental-decimal-floating-point -fsyntax-only -verify=expected,c %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -x c++ -std=c++2c -fexperimental-decimal-floating-point -fsyntax-only -verify=expected,cxx %s // _Decimal32, _Decimal64, and _Decimal128 are never keywords in C++. _Decimal32 d32; // cxx-error {{unknown type name '_Decimal32'}} _Decimal64 d64; // cxx-error {{unknown type name '_Decimal64'}} _Decimal128 d28; // cxx-error {{unknown type name '_Decimal128'}} + +// DFP types are available via the GNU mode attribute in both C and C++. +typedef float __attribute__((mode(SD))) D32; +typedef float __attribute__((mode(DD))) D64; +typedef float __attribute__((mode(TD))) D128; + +// The GNU mode attribute requires a floating point base type for DFP types. +// These are ok. +long double __attribute((mode(SD))) ldamsd; +double __attribute((mode(DD))) damdd; +_Float16 __attribute((mode(SD))) f16amsd; +__bf16 __attribute((mode(SD))) bf16amsd; +__float128 __attribute((mode(TD))) f128amtd; +// These are not ok. +void __attribute((mode(SD))) vamsd; // expected-error {{type of machine mode does not match type of base type}} +int __attribute((mode(DD))) iamdd; // expected-error {{type of machine mode does not match type of base type}} +int* __attribute((mode(TD))) ipamtd; // expected-error {{mode attribute only supported for integer and floating-point types}} +float __attribute((mode(TD))) *fapmtd; // expected-error {{mode attribute only supported for integer and floating-point types}} + +// DFP types may be used as vector elements, but declaration form is restricted. +float __attribute__((mode(V4SD))) famv4sd; // expected-warning {{deprecated; use the 'vector_size' attribute instead}} +float __attribute__((mode(SD))) __attribute__((vector_size(16))) famsdv16; +D64 __attribute__((vector_size(16))) d64av16; + +// DFP types are not allowed as elements of complex types. +D32 _Complex d32c; // expected-error {{'_Complex type-name' is invalid}} +_Decimal32 _Complex kd32c; // c-error {{'_Complex _Decimal32' is invalid}} \ + cxx-error {{unknown type name '_Decimal32'}} + +_Static_assert(sizeof(D32) == 4); +_Static_assert(sizeof(D64) == 8); +_Static_assert(sizeof(D128) == 16); + +_Static_assert(_Alignof(D32) == 4); +_Static_assert(_Alignof(D64) == 8); +_Static_assert(_Alignof(D128) == 16); + +struct s { + D32 d32; + D64 d64; + D128 d128; + union { + D32 ud32; + D64 ud64; + D128 ud128; + }; +}; + +struct bitfield { + D32 d32 : 32; // expected-error {{bit-field 'd32' has non-integral type}} + D64 d64 : 64; // expected-error {{bit-field 'd64' has non-integral type}} + D128 d128 : 128; // expected-error {{bit-field 'd128' has non-integral type}} +}; + +D32 test_d32(D32 d32) { + return d32; +} + +D64 test_d64(D64 d64) { + return d64; +} + +D128 test_d128(D128 d128) { + return d128; +} + +void test_builtin_complex(D32 d32) { + __auto_type lv = __builtin_complex(d32, d32); // expected-error {{'_Complex _Decimal32' is invalid}} +} + +void test_generic(D32 d32, D64 d64, D128 d128) { + (void)_Generic(d32, D64 : 0, D128 : 0); // expected-error-re {{controlling expression type {{.*}} not compatible with any generic association type}} + (void)_Generic(d64, D32 : 0, D128 : 0); // expected-error-re {{controlling expression type {{.*}} not compatible with any generic association type}} + (void)_Generic(d128, D32 : 0, D64 : 0); // expected-error-re {{controlling expression type {{.*}} not compatible with any generic association type}} + _Static_assert(_Generic(d32, D64 : 0, D128 : 0, default : 1) == 1); + _Static_assert(_Generic(d64, D32 : 0, D128 : 0, default : 1) == 1); + _Static_assert(_Generic(d128, D32 : 0, D64 : 0, default : 1) == 1); + _Static_assert(_Generic(d32, D32 : 1, D64 : 0, D128 : 0) == 1); + _Static_assert(_Generic(d64, D32 : 0, D64 : 1, D128 : 0) == 1); + _Static_assert(_Generic(d128, D32 : 0, D64 : 0, D128 : 1) == 1); +} diff --git a/clang/test/SemaCXX/dfp-types.cpp b/clang/test/SemaCXX/dfp-types.cpp new file mode 100644 index 0000000000000..9a26aefab20f6 --- /dev/null +++ b/clang/test/SemaCXX/dfp-types.cpp @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++2c -fexperimental-decimal-floating-point -fsyntax-only -verify %s + +using D32 = float __attribute__((mode(SD))); +using D64 = float __attribute__((mode(DD))); +using D128 = float __attribute__((mode(TD))); + +// Dependent type specifiers for the GNU mode attribute base type are ok, but +// must be of a valid type when instantiated. +template +T __attribute((mode(SD))) dtamsd; // expected-error {{type of machine mode does not match type of base type}} +auto g1 = dtamsd; +auto g2 = dtamsd; +auto g3 = dtamsd; +auto g4 = dtamsd; // expected-note {{in instantiation of variable template specialization 'dtamsd' requested here}}