diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10714,8 +10714,8 @@ def err_omp_wrong_dependency_iterator_type : Error< "expected an integer or a pointer type of the outer loop counter '%0' for non-rectangular nests">; def err_target_unsupported_type - : Error<"%0 requires %select{|%2 bit size}1 %3 type support, but target " - "'%4' does not support it">; + : Error<"%0 requires %select{|%2 bit size}1 %3 %select{|return }4type support," + " but target '%5' does not support it">; def err_omp_lambda_capture_in_declare_target_not_to : Error< "variable captured in declare target region must appear in a to clause">; def err_omp_device_type_mismatch : Error< diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -203,6 +203,8 @@ bool HasFloat16; bool HasBFloat16; bool HasIbm128; + bool HasLongDouble; + bool HasFPReturn; bool HasStrictFP; unsigned char MaxAtomicPromoteWidth, MaxAtomicInlineWidth; @@ -601,6 +603,13 @@ /// Determine whether the __ibm128 type is supported on this target. virtual bool hasIbm128Type() const { return HasIbm128; } + /// Determine whether the long double type is supported on this target. + virtual bool hasLongDoubleType() const { return HasLongDouble; } + + /// Determine whether return of a floating point value is supported + /// on this target. + virtual bool hasFPReturn() const { return HasFPReturn; } + /// Determine whether constrained floating point is supported on this target. virtual bool hasStrictFP() const { return HasStrictFP; } diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -37,6 +37,8 @@ HasIbm128 = false; HasFloat16 = false; HasBFloat16 = false; + HasLongDouble = true; + HasFPReturn = true; HasStrictFP = false; PointerWidth = PointerAlign = 32; BoolWidth = BoolAlign = 8; diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -144,6 +144,7 @@ bool HasTSXLDTRK = false; bool HasUINTR = false; bool HasCRC32 = false; + bool HasX87 = false; protected: llvm::X86::CPUKind CPU = llvm::X86::CK_None; diff --git a/clang/lib/Basic/Targets/X86.cpp b/clang/lib/Basic/Targets/X86.cpp --- a/clang/lib/Basic/Targets/X86.cpp +++ b/clang/lib/Basic/Targets/X86.cpp @@ -338,6 +338,8 @@ HasUINTR = true; } else if (Feature == "+crc32") { HasCRC32 = true; + } else if (Feature == "+x87") { + HasX87 = true; } X86SSEEnum Level = llvm::StringSwitch(Feature) @@ -379,6 +381,14 @@ SimdDefaultAlign = hasFeature("avx512f") ? 512 : hasFeature("avx") ? 256 : 128; + + if (!HasX87) { + if (LongDoubleFormat == &llvm::APFloat::x87DoubleExtended()) + HasLongDouble = false; + if (getTriple().getArch() == llvm::Triple::x86) + HasFPReturn = false; + } + return true; } @@ -1038,6 +1048,7 @@ .Case("x86", true) .Case("x86_32", getTriple().getArch() == llvm::Triple::x86) .Case("x86_64", getTriple().getArch() == llvm::Triple::x86_64) + .Case("x87", HasX87) .Case("xop", XOPLevel >= XOP) .Case("xsave", HasXSAVE) .Case("xsavec", HasXSAVEC) diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1855,9 +1855,6 @@ } void Sema::checkTypeSupport(QualType Ty, SourceLocation Loc, ValueDecl *D) { - if (!LangOpts.SYCLIsDevice && !(LangOpts.OpenMP && LangOpts.OpenMPIsDevice)) - return; - if (isUnevaluatedContext() || Ty.isNull()) return; @@ -1880,7 +1877,7 @@ FunctionDecl *FD = isa(C) ? cast(C) : dyn_cast_or_null(D); - auto CheckType = [&](QualType Ty) { + auto CheckDeviceType = [&](QualType Ty) { if (Ty->isDependentType()) return; @@ -1892,7 +1889,7 @@ else PD << "expression"; targetDiag(Loc, PD, FD) - << false /*show bit size*/ << 0 /*bitsize*/ + << false /*show bit size*/ << 0 /*bitsize*/ << false /*return*/ << Ty << Context.getTargetInfo().getTriple().str(); } return; @@ -1925,6 +1922,49 @@ if (targetDiag(Loc, PD, FD) << true /*show bit size*/ << static_cast(Context.getTypeSize(Ty)) << Ty + << false /*return*/ << Context.getTargetInfo().getTriple().str()) { + if (D) + D->setInvalidDecl(); + } + if (D) + targetDiag(D->getLocation(), diag::note_defined_here, FD) << D; + } + }; + + auto CheckType = [&](QualType Ty, bool IsRetTy = false) { + if (LangOpts.SYCLIsDevice || (LangOpts.OpenMP && LangOpts.OpenMPIsDevice)) + CheckDeviceType(Ty); + + QualType UnqualTy = Ty.getCanonicalType().getUnqualifiedType(); + const TargetInfo &TI = Context.getTargetInfo(); + if (!TI.hasLongDoubleType() && UnqualTy == Context.LongDoubleTy) { + PartialDiagnostic PD = PDiag(diag::err_target_unsupported_type); + if (D) + PD << D; + else + PD << "expression"; + + if (Diag(Loc, PD, FD) + << false /*show bit size*/ << 0 << Ty << false /*return*/ + << Context.getTargetInfo().getTriple().str()) { + if (D) + D->setInvalidDecl(); + } + if (D) + targetDiag(D->getLocation(), diag::note_defined_here, FD) << D; + } + + bool IsDouble = UnqualTy == Context.DoubleTy; + bool IsFloat = UnqualTy == Context.FloatTy; + if (IsRetTy && !TI.hasFPReturn() && (IsDouble || IsFloat)) { + PartialDiagnostic PD = PDiag(diag::err_target_unsupported_type); + if (D) + PD << D; + else + PD << "expression"; + + if (Diag(Loc, PD, FD) + << false /*show bit size*/ << 0 << Ty << true /*return*/ << Context.getTargetInfo().getTriple().str()) { if (D) D->setInvalidDecl(); @@ -1935,14 +1975,13 @@ }; CheckType(Ty); - if (const auto *FPTy = dyn_cast(Ty)) { for (const auto &ParamTy : FPTy->param_types()) CheckType(ParamTy); - CheckType(FPTy->getReturnType()); + CheckType(FPTy->getReturnType(), /*IsRetTy=*/true); } if (const auto *FNPTy = dyn_cast(Ty)) - CheckType(FNPTy->getReturnType()); + CheckType(FNPTy->getReturnType(), /*IsRetTy=*/true); } /// Looks through the macro-expansion chain for the given diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -9570,8 +9570,6 @@ } } - checkTypeSupport(NewFD->getType(), D.getBeginLoc(), NewFD); - if (!getLangOpts().CPlusPlus) { // Perform semantic checking on the function declaration. if (!NewFD->isInvalidDecl() && NewFD->isMain()) @@ -14857,6 +14855,9 @@ DeclsToCheckForDeferredDiags.insert(FD); } + if (FD && !FD->isDeleted()) + checkTypeSupport(FD->getType(), FD->getLocation(), FD); + return dcl; } diff --git a/clang/test/Sema/x86-no-x87.cpp b/clang/test/Sema/x86-no-x87.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Sema/x86-no-x87.cpp @@ -0,0 +1,164 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -triple i686-linux-gnu -target-feature -x87 -DRET_ERROR +// RUN: %clang_cc1 -fsyntax-only -verify %s -triple i686-linux-gnu -DNOERROR + +#ifdef NOERROR +// expected-no-diagnostics +#endif + +typedef long double long_double; + +// Declaration is fine, unless it is called or defined. +double decl(long_double x, long_double y); + +template +T decl_ld_del(T); + +// No code is generated for deleted functions +long_double decl_ld_del(long_double) = delete; +double decl_ld_del(double) = delete; +float decl_ld_del(float) = delete; + +#ifndef NOERROR +// expected-error@+4{{'def' requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}} +// expected-note@+3{{'def' defined here}} +// expected-note@+2{{'x' defined here}} +#endif +int def(long_double x) { +#ifndef NOERROR +// expected-error@+2{{'x' requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}} +#endif + return (int)x; +} + +#ifndef NOERROR +// expected-note@+3{{'ld_args' defined here}} +// expected-note@+2{{'ld_args' defined here}} +#endif +int ld_args(long_double x, long_double y); + +int call1(float x, float y) { +#ifndef NOERROR + // expected-error@+2 2{{'ld_args' requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}} +#endif + return ld_args(x, y); +} + +#ifndef NOERROR +// expected-note@+2{{'ld_ret' defined here}} +#endif +long_double ld_ret(double x, double y); + +int call2(float x, float y) { +#ifndef NOERROR + // expected-error@+2{{'ld_ret' requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}} +#endif + return (int)ld_ret(x, y); +} + +int binop(double x, double y) { +#ifndef NOERROR + // expected-error@+2 2{{expression requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}} +#endif + double z = (long_double)x * (long_double)y; + return (int)z; +} + +void assign1(long_double *ret, double x) { +#ifndef NOERROR + // expected-error@+2{{expression requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}} +#endif + *ret = x; +} + +struct st_long_double1 { +#ifndef NOERROR + // expected-note@+2{{'ld' defined here}} +#endif + long_double ld; +}; + +struct st_long_double2 { +#ifndef NOERROR + // expected-note@+2{{'ld' defined here}} +#endif + long_double ld; +}; + +struct st_long_double3 { +#ifndef NOERROR + // expected-note@+2{{'ld' defined here}} +#endif + long_double ld; +}; + +void assign2() { + struct st_long_double1 st; +#ifndef NOERROR + // expected-error@+3{{expression requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}} + // expected-error@+2{{'ld' requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}} +#endif + st.ld = 0.42; +} + +void assign3() { + struct st_long_double2 st; +#ifndef NOERROR + // expected-error@+3{{expression requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}} + // expected-error@+2{{'ld' requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}} +#endif + st.ld = 42; +} + +void assign4(double d) { + struct st_long_double3 st; +#ifndef NOERROR + // expected-error@+3{{expression requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}} + // expected-error@+2{{'ld' requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}} +#endif + st.ld = d; +} + +void assign5() { + // unused variable declaration is fine + long_double ld = 0.42; +} + +#ifndef NOERROR +// expected-note@+3{{'d_ret1' defined here}} +// expected-error@+2{{'d_ret1' requires 'double' return type support, but target 'i686-unknown-linux-gnu' does not support it}} +#endif +double d_ret1(float x) { + return 0.0; +} + +#ifndef NOERROR +// expected-note@+2{{'d_ret2' defined here}} +#endif +double d_ret2(float x); + +int d_ret3(float x) { +#ifndef NOERROR + // expected-error@+2{{'d_ret2' requires 'double' return type support, but target 'i686-unknown-linux-gnu' does not support it}} +#endif + return (int)d_ret2(x); +} + +#ifndef NOERROR +// expected-note@+3{{'f_ret1' defined here}} +// expected-error@+2{{'f_ret1' requires 'float' return type support, but target 'i686-unknown-linux-gnu' does not support it}} +#endif +float f_ret1(float x) { + return 0.0f; +} + +#ifndef NOERROR +// expected-note@+2{{'f_ret2' defined here}} +#endif +float f_ret2(float x); + +int f_ret3(float x) { +#ifndef NOERROR + // expected-error@+2{{'f_ret2' requires 'float' return type support, but target 'i686-unknown-linux-gnu' does not support it}} +#endif + return (int)f_ret2(x); +} diff --git a/clang/test/Sema/x86_64-no-x87.cpp b/clang/test/Sema/x86_64-no-x87.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Sema/x86_64-no-x87.cpp @@ -0,0 +1,145 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -triple x86_64-linux-gnu -target-feature -x87 +// RUN: %clang_cc1 -fsyntax-only -verify %s -triple x86_64-linux-gnu -DNOERROR + +#ifdef NOERROR +// expected-no-diagnostics +#endif + +typedef long double long_double; + +// Declaration is fine, unless it is called or defined. +double decl(long_double x, long_double y); + +template +T decl_ld_del(T); + +// No code is generated for deleted functions +long_double decl_ld_del(long_double) = delete; +double decl_ld_del(double) = delete; +float decl_ld_del(float) = delete; + +#ifndef NOERROR +// expected-error@+4{{'def' requires 'long_double' (aka 'long double') type support, but target 'x86_64-unknown-linux-gnu' does not support it}} +// expected-note@+3{{'def' defined here}} +// expected-note@+2{{'x' defined here}} +#endif +int def(long_double x) { +#ifndef NOERROR +// expected-error@+2{{'x' requires 'long_double' (aka 'long double') type support, but target 'x86_64-unknown-linux-gnu' does not support it}} +#endif + return (int)x; +} + +#ifndef NOERROR +// expected-note@+3{{'ld_args' defined here}} +// expected-note@+2{{'ld_args' defined here}} +#endif +int ld_args(long_double x, long_double y); + +int call1(float x, float y) { +#ifndef NOERROR + // expected-error@+2 2{{'ld_args' requires 'long_double' (aka 'long double') type support, but target 'x86_64-unknown-linux-gnu' does not support it}} +#endif + return ld_args(x, y); +} + +#ifndef NOERROR +// expected-note@+2{{'ld_ret' defined here}} +#endif +long_double ld_ret(double x, double y); + +int call2(float x, float y) { +#ifndef NOERROR + // expected-error@+2{{'ld_ret' requires 'long_double' (aka 'long double') type support, but target 'x86_64-unknown-linux-gnu' does not support it}} +#endif + return (int)ld_ret(x, y); +} + +int binop(double x, double y) { +#ifndef NOERROR + // expected-error@+2 2{{expression requires 'long_double' (aka 'long double') type support, but target 'x86_64-unknown-linux-gnu' does not support it}} +#endif + double z = (long_double)x * (long_double)y; + return (int)z; +} + +void assign1(long_double *ret, double x) { +#ifndef NOERROR + // expected-error@+2{{expression requires 'long_double' (aka 'long double') type support, but target 'x86_64-unknown-linux-gnu' does not support it}} +#endif + *ret = x; +} + +struct st_long_double1 { +#ifndef NOERROR + // expected-note@+2{{'ld' defined here}} +#endif + long_double ld; +}; + +struct st_long_double2 { +#ifndef NOERROR + // expected-note@+2{{'ld' defined here}} +#endif + long_double ld; +}; + +struct st_long_double3 { +#ifndef NOERROR + // expected-note@+2{{'ld' defined here}} +#endif + long_double ld; +}; + +void assign2() { + struct st_long_double1 st; +#ifndef NOERROR + // expected-error@+3{{expression requires 'long_double' (aka 'long double') type support, but target 'x86_64-unknown-linux-gnu' does not support it}} + // expected-error@+2{{'ld' requires 'long_double' (aka 'long double') type support, but target 'x86_64-unknown-linux-gnu' does not support it}} +#endif + st.ld = 0.42; +} + +void assign3() { + struct st_long_double2 st; +#ifndef NOERROR + // expected-error@+3{{expression requires 'long_double' (aka 'long double') type support, but target 'x86_64-unknown-linux-gnu' does not support it}} + // expected-error@+2{{'ld' requires 'long_double' (aka 'long double') type support, but target 'x86_64-unknown-linux-gnu' does not support it}} +#endif + st.ld = 42; +} + +void assign4(double d) { + struct st_long_double3 st; +#ifndef NOERROR + // expected-error@+3{{expression requires 'long_double' (aka 'long double') type support, but target 'x86_64-unknown-linux-gnu' does not support it}} + // expected-error@+2{{'ld' requires 'long_double' (aka 'long double') type support, but target 'x86_64-unknown-linux-gnu' does not support it}} +#endif + st.ld = d; +} + +void assign5() { + // unused variable declaration is fine + long_double ld = 0.42; +} + +// Double and Float return type on x86_64 do not use x87 registers +double d_ret1(float x) { + return 0.0; +} + +double d_ret2(float x); + +int d_ret3(float x) { + return (int)d_ret2(x); +} + +float f_ret1(float x) { + return 0.0f; +} + +float f_ret2(float x); + +int f_ret3(float x) { + return (int)f_ret2(x); +} diff --git a/clang/test/SemaSYCL/float128.cpp b/clang/test/SemaSYCL/float128.cpp --- a/clang/test/SemaSYCL/float128.cpp +++ b/clang/test/SemaSYCL/float128.cpp @@ -22,6 +22,8 @@ C.field1 = A; } +long double ld_func(long double arg); + void usage() { // expected-note@+1 3{{'A' defined here}} __float128 A; @@ -48,6 +50,9 @@ float F2 = 0.1f; // expected-error@+1 3{{expression requires 128 bit size '__float128' type support, but target 'spir64' does not support it}} float F3 = ((__float128)F1 * (__float128)F2) / 2.0f; + + // assume that long double is supported + float F4 = ld_func(F3); }; // expected-note@+1 {{called by 'usage'}}