asavonic updated this revision to Diff 381538. asavonic retitled this revision from "[X86][Draft] Disable long double type for -mno-x87 option" to "[X86][clang] Disable long double type for -mno-x87 option". asavonic edited the summary of this revision. asavonic added a comment. Herald added a reviewer: jdoerfert. Herald added a subscriber: sstefan1.
Rebased the patch on top of D109315 <https://reviews.llvm.org/D109315>. Since the diagnostic is now shared with other targets (SYCL and OpenMP), I decided to drop all assumptions on optimizations that we previously discussed. There seems to be no actual use case where it is important to have them (other than compatibility with GCC), and they make rules a bit inconsistent. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D98895/new/ https://reviews.llvm.org/D98895 Files: clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Basic/TargetInfo.h clang/lib/Basic/TargetInfo.cpp clang/lib/Basic/Targets/X86.cpp clang/lib/Basic/Targets/X86.h clang/lib/Sema/Sema.cpp clang/lib/Sema/SemaDecl.cpp clang/test/Sema/x86-no-x87.c clang/test/Sema/x86_64-no-x87.c
Index: clang/test/Sema/x86_64-no-x87.c =================================================================== --- /dev/null +++ clang/test/Sema/x86_64-no-x87.c @@ -0,0 +1,137 @@ +// 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); + +#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); +} Index: clang/test/Sema/x86-no-x87.c =================================================================== --- /dev/null +++ clang/test/Sema/x86-no-x87.c @@ -0,0 +1,157 @@ +// 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); + +#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 2{{'ld_ret' defined here}} +#endif +long_double ld_ret(double x, double y); + +int call2(float x, float y) { +#ifndef NOERROR + // expected-error@+3{{'ld_ret' requires 'long_double' (aka 'long double') return type support, but target 'i686-unknown-linux-gnu' does not support it}} + // 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); +} Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -9570,7 +9570,8 @@ } } - checkTypeSupport(NewFD->getType(), D.getBeginLoc(), NewFD); + if (D.getFunctionDefinitionKind() != FunctionDefinitionKind::Declaration) + checkTypeSupport(NewFD->getType(), D.getBeginLoc(), NewFD); if (!getLangOpts().CPlusPlus) { // Perform semantic checking on the function declaration. Index: clang/lib/Sema/Sema.cpp =================================================================== --- clang/lib/Sema/Sema.cpp +++ 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<FunctionDecl>(C) ? cast<FunctionDecl>(C) : dyn_cast_or_null<FunctionDecl>(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,47 @@ if (targetDiag(Loc, PD, FD) << true /*show bit size*/ << static_cast<unsigned>(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; + } + + if (IsRetTy && !TI.hasFPReturn() && Ty->isFloatingType()) { + 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(); @@ -1939,10 +1977,10 @@ if (const auto *FPTy = dyn_cast<FunctionProtoType>(Ty)) { for (const auto &ParamTy : FPTy->param_types()) CheckType(ParamTy); - CheckType(FPTy->getReturnType()); + CheckType(FPTy->getReturnType(), /*IsRetTy=*/true); } if (const auto *FNPTy = dyn_cast<FunctionNoProtoType>(Ty)) - CheckType(FNPTy->getReturnType()); + CheckType(FNPTy->getReturnType(), /*IsRetTy=*/true); } /// Looks through the macro-expansion chain for the given Index: clang/lib/Basic/Targets/X86.h =================================================================== --- clang/lib/Basic/Targets/X86.h +++ 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; Index: clang/lib/Basic/Targets/X86.cpp =================================================================== --- clang/lib/Basic/Targets/X86.cpp +++ 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<X86SSEEnum>(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) Index: clang/lib/Basic/TargetInfo.cpp =================================================================== --- clang/lib/Basic/TargetInfo.cpp +++ 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; Index: clang/include/clang/Basic/TargetInfo.h =================================================================== --- clang/include/clang/Basic/TargetInfo.h +++ 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; } Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ 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<
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits