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 @@ -200,6 +200,7 @@ bool HasFloat128; bool HasFloat16; bool HasBFloat16; + bool HasLongDouble; bool HasStrictFP; unsigned char MaxAtomicPromoteWidth, MaxAtomicInlineWidth; @@ -593,6 +594,9 @@ /// Determine whether the _BFloat16 type is supported on this target. virtual bool hasBFloat16Type() const { return HasBFloat16; } + /// Determine whether the long double type is supported on this target. + virtual bool hasLongDoubleType() const { return HasLongDouble; } + /// 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 @@ -36,6 +36,7 @@ HasFloat128 = false; HasFloat16 = false; HasBFloat16 = false; + HasLongDouble = 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 @@ -140,6 +140,7 @@ bool HasSERIALIZE = false; bool HasTSXLDTRK = false; bool HasUINTR = 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 @@ -314,6 +314,8 @@ HasTSXLDTRK = true; } else if (Feature == "+uintr") { HasUINTR = true; + } else if (Feature == "+x87") { + HasX87 = true; } X86SSEEnum Level = llvm::StringSwitch(Feature) @@ -355,6 +357,11 @@ SimdDefaultAlign = hasFeature("avx512f") ? 512 : hasFeature("avx") ? 256 : 128; + + if (!HasX87 && LongDoubleFormat == &llvm::APFloat::x87DoubleExtended()) { + HasLongDouble = false; + } + return true; } @@ -1004,6 +1011,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/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4756,6 +4756,17 @@ CheckAbsoluteValueFunction(TheCall, FDecl); CheckMaxUnsignedZero(TheCall, FDecl); + if (!Context.getTargetInfo().hasLongDoubleType()) { + if (Proto->getReturnType().getCanonicalType() == Context.LongDoubleTy) { + Diag(TheCall->getExprLoc(), diag::err_type_unsupported) << "long double"; + } + for (ParmVarDecl *Param : FDecl->parameters()) { + if (Param->getType().getCanonicalType() == Context.LongDoubleTy) { + Diag(Param->getLocation(), diag::err_type_unsupported) << "long double"; + } + } + } + if (getLangOpts().ObjC) DiagnoseCStringFormatDirectiveInCFAPI(*this, FDecl, Args, NumArgs); 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 @@ -7270,6 +7270,11 @@ } } + if (!Context.getTargetInfo().hasLongDoubleType() && + NewVD->getType().getCanonicalType() == Context.LongDoubleTy) { + Diag(NewVD->getLocation(), diag::err_type_unsupported) << "long double"; + } + // Handle attributes prior to checking for duplicates in MergeVarDecl ProcessDeclAttributes(S, NewVD, D); @@ -14223,6 +14228,17 @@ CheckParmsForFunctionDef(FD->parameters(), /*CheckParameterNames=*/true); + if (!Context.getTargetInfo().hasLongDoubleType()) { + if (ResultType.getCanonicalType() == Context.LongDoubleTy) { + Diag(FD->getLocation(), diag::err_type_unsupported) << "long double"; + } + for (ParmVarDecl *Param : FD->parameters()) { + if (Param->getType().getCanonicalType() == Context.LongDoubleTy) { + Diag(Param->getLocation(), diag::err_type_unsupported) << "long double"; + } + } + } + // Add non-parameter declarations already in the function to the current // scope. if (FnBodyScope) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -14002,6 +14002,26 @@ } } + if (!Context.getTargetInfo().hasLongDoubleType()) { + QualType LHSTy = LHSExpr->getType(); + QualType RHSTy = RHSExpr->getType(); + + // Allow assignment of a literal, because it compiles to just a + // store of a constant. + bool IsLiteralAssign = + (Opc == BO_Assign) && + (isa(RHSExpr) || isa(RHSExpr)); + + bool HaveLongDoubleOp = + (LHSTy.getCanonicalType() == Context.LongDoubleTy) || + (RHSTy.getCanonicalType() == Context.LongDoubleTy); + + if (HaveLongDoubleOp && !IsLiteralAssign) { + Diag(OpLoc, diag::err_type_unsupported) << "long double"; + return ExprError(); + } + } + switch (Opc) { case BO_Assign: ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, QualType()); @@ -14840,6 +14860,13 @@ if (Opc != UO_AddrOf && Opc != UO_Deref) CheckArrayAccess(Input.get()); + if (!Context.getTargetInfo().hasLongDoubleType() && + (resultType.getCanonicalType() == Context.LongDoubleTy || + InputExpr->getType().getCanonicalType() == Context.LongDoubleTy)) { + Diag(OpLoc, diag::err_type_unsupported) << "long double"; + return ExprError(); + } + auto *UO = UnaryOperator::Create(Context, Input.get(), Opc, resultType, VK, OK, OpLoc, CanOverflow, CurFPFeatureOverrides()); diff --git a/clang/test/Sema/x86-no-x87.c b/clang/test/Sema/x86-no-x87.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/x86-no-x87.c @@ -0,0 +1,73 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -triple x86_64-linux-gnu -target-feature -x87 +// RUN: %clang_cc1 -fsyntax-only -verify %s -triple i686-linux-gnu -target-feature -x87 +// RUN: %clang_cc1 -fsyntax-only -verify %s -triple x86_64-windows-msvc -target-feature -x87 -DNOERROR +// RUN: %clang_cc1 -fsyntax-only -verify %s -triple x86_64-linux-gnu -DNOERROR + +#ifdef NOERROR +// expected-no-diagnostics +#endif + +typedef long double long_double; + +#ifndef NOERROR +// expected-error@+3{{long double is not supported on this target}} +// expected-error@+2{{long double is not supported on this target}} +#endif +double ld_args(long_double x, long_double y); + +long_double ld_ret(double x, double y); + +double args(double x, double y) { + return ld_args(x, y); +} + +double ret(double x, double y) { +#ifndef NOERROR + // expected-error@+2{{long double is not supported on this target}} +#endif + return ld_ret(x, y); +} + +double binop(double x, double y) { +#ifndef NOERROR + // expected-error@+2{{long double is not supported on this target}} +#endif + double z = (long_double)x * (long_double)y; + return z; +} + +void assign1(long_double *ret, double x) { +#ifndef NOERROR + // expected-error@+2{{long double is not supported on this target}} +#endif + *ret = x; +} + +struct st_long_double { + long_double ld; +}; + +void assign2() { + struct st_long_double st; + st.ld = 0.42; +} + +void assign3() { + struct st_long_double st; + st.ld = 42; +} + +void assign4(double d) { + struct st_long_double st; +#ifndef NOERROR + // expected-error@+2{{long double is not supported on this target}} +#endif + st.ld = d; +} + +void assign5() { +#ifndef NOERROR + // expected-error@+2{{long double is not supported on this target}} +#endif + long_double ld = 0.42; +}