Skip to content

Commit a8083d4

Browse files
committed
[X86][clang] Disable long double type for -mno-x87 option
This patch attempts to fix a compiler crash that occurs when long double type is used with -mno-x87 compiler option. The option disables x87 target feature, which in turn disables x87 registers, so CG cannot select them for x86_fp80 LLVM IR type. Long double is lowered as x86_fp80 for some targets, so it leads to a crash. The option seems to contradict the SystemV ABI, which requires long double to be represented as a 80-bit floating point, and it also requires to use x87 registers. To avoid that, `long double` type is disabled when -mno-x87 option is set. In addition to that, `float` and `double` also use x87 registers for return values on 32-bit x86, so they are disabled as well. Differential Revision: https://reviews.llvm.org/D98895
1 parent 3d32218 commit a8083d4

File tree

10 files changed

+389
-12
lines changed

10 files changed

+389
-12
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10714,8 +10714,8 @@ def err_omp_invariant_or_linear_dependency : Error<
1071410714
def err_omp_wrong_dependency_iterator_type : Error<
1071510715
"expected an integer or a pointer type of the outer loop counter '%0' for non-rectangular nests">;
1071610716
def err_target_unsupported_type
10717-
: Error<"%0 requires %select{|%2 bit size}1 %3 type support, but target "
10718-
"'%4' does not support it">;
10717+
: Error<"%0 requires %select{|%2 bit size}1 %3 %select{|return }4type support,"
10718+
" but target '%5' does not support it">;
1071910719
def err_omp_lambda_capture_in_declare_target_not_to : Error<
1072010720
"variable captured in declare target region must appear in a to clause">;
1072110721
def err_omp_device_type_mismatch : Error<

clang/include/clang/Basic/TargetInfo.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ class TargetInfo : public virtual TransferrableTargetInfo,
203203
bool HasFloat16;
204204
bool HasBFloat16;
205205
bool HasIbm128;
206+
bool HasLongDouble;
207+
bool HasFPReturn;
206208
bool HasStrictFP;
207209

208210
unsigned char MaxAtomicPromoteWidth, MaxAtomicInlineWidth;
@@ -601,6 +603,13 @@ class TargetInfo : public virtual TransferrableTargetInfo,
601603
/// Determine whether the __ibm128 type is supported on this target.
602604
virtual bool hasIbm128Type() const { return HasIbm128; }
603605

606+
/// Determine whether the long double type is supported on this target.
607+
virtual bool hasLongDoubleType() const { return HasLongDouble; }
608+
609+
/// Determine whether return of a floating point value is supported
610+
/// on this target.
611+
virtual bool hasFPReturn() const { return HasFPReturn; }
612+
604613
/// Determine whether constrained floating point is supported on this target.
605614
virtual bool hasStrictFP() const { return HasStrictFP; }
606615

clang/lib/Basic/TargetInfo.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : TargetOpts(), Triple(T) {
3737
HasIbm128 = false;
3838
HasFloat16 = false;
3939
HasBFloat16 = false;
40+
HasLongDouble = true;
41+
HasFPReturn = true;
4042
HasStrictFP = false;
4143
PointerWidth = PointerAlign = 32;
4244
BoolWidth = BoolAlign = 8;

clang/lib/Basic/Targets/X86.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,8 @@ bool X86TargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
338338
HasUINTR = true;
339339
} else if (Feature == "+crc32") {
340340
HasCRC32 = true;
341+
} else if (Feature == "+x87") {
342+
HasX87 = true;
341343
}
342344

343345
X86SSEEnum Level = llvm::StringSwitch<X86SSEEnum>(Feature)
@@ -379,6 +381,14 @@ bool X86TargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
379381

380382
SimdDefaultAlign =
381383
hasFeature("avx512f") ? 512 : hasFeature("avx") ? 256 : 128;
384+
385+
if (!HasX87) {
386+
if (LongDoubleFormat == &llvm::APFloat::x87DoubleExtended())
387+
HasLongDouble = false;
388+
if (getTriple().getArch() == llvm::Triple::x86)
389+
HasFPReturn = false;
390+
}
391+
382392
return true;
383393
}
384394

@@ -1038,6 +1048,7 @@ bool X86TargetInfo::hasFeature(StringRef Feature) const {
10381048
.Case("x86", true)
10391049
.Case("x86_32", getTriple().getArch() == llvm::Triple::x86)
10401050
.Case("x86_64", getTriple().getArch() == llvm::Triple::x86_64)
1051+
.Case("x87", HasX87)
10411052
.Case("xop", XOPLevel >= XOP)
10421053
.Case("xsave", HasXSAVE)
10431054
.Case("xsavec", HasXSAVEC)

clang/lib/Basic/Targets/X86.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo {
144144
bool HasTSXLDTRK = false;
145145
bool HasUINTR = false;
146146
bool HasCRC32 = false;
147+
bool HasX87 = false;
147148

148149
protected:
149150
llvm::X86::CPUKind CPU = llvm::X86::CK_None;

clang/lib/Sema/Sema.cpp

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1855,9 +1855,6 @@ Sema::SemaDiagnosticBuilder Sema::Diag(SourceLocation Loc, unsigned DiagID,
18551855
}
18561856

18571857
void Sema::checkTypeSupport(QualType Ty, SourceLocation Loc, ValueDecl *D) {
1858-
if (!LangOpts.SYCLIsDevice && !(LangOpts.OpenMP && LangOpts.OpenMPIsDevice))
1859-
return;
1860-
18611858
if (isUnevaluatedContext() || Ty.isNull())
18621859
return;
18631860

@@ -1880,7 +1877,7 @@ void Sema::checkTypeSupport(QualType Ty, SourceLocation Loc, ValueDecl *D) {
18801877
FunctionDecl *FD = isa<FunctionDecl>(C) ? cast<FunctionDecl>(C)
18811878
: dyn_cast_or_null<FunctionDecl>(D);
18821879

1883-
auto CheckType = [&](QualType Ty) {
1880+
auto CheckDeviceType = [&](QualType Ty) {
18841881
if (Ty->isDependentType())
18851882
return;
18861883

@@ -1892,7 +1889,7 @@ void Sema::checkTypeSupport(QualType Ty, SourceLocation Loc, ValueDecl *D) {
18921889
else
18931890
PD << "expression";
18941891
targetDiag(Loc, PD, FD)
1895-
<< false /*show bit size*/ << 0 /*bitsize*/
1892+
<< false /*show bit size*/ << 0 /*bitsize*/ << false /*return*/
18961893
<< Ty << Context.getTargetInfo().getTriple().str();
18971894
}
18981895
return;
@@ -1925,6 +1922,49 @@ void Sema::checkTypeSupport(QualType Ty, SourceLocation Loc, ValueDecl *D) {
19251922
if (targetDiag(Loc, PD, FD)
19261923
<< true /*show bit size*/
19271924
<< static_cast<unsigned>(Context.getTypeSize(Ty)) << Ty
1925+
<< false /*return*/ << Context.getTargetInfo().getTriple().str()) {
1926+
if (D)
1927+
D->setInvalidDecl();
1928+
}
1929+
if (D)
1930+
targetDiag(D->getLocation(), diag::note_defined_here, FD) << D;
1931+
}
1932+
};
1933+
1934+
auto CheckType = [&](QualType Ty, bool IsRetTy = false) {
1935+
if (LangOpts.SYCLIsDevice || (LangOpts.OpenMP && LangOpts.OpenMPIsDevice))
1936+
CheckDeviceType(Ty);
1937+
1938+
QualType UnqualTy = Ty.getCanonicalType().getUnqualifiedType();
1939+
const TargetInfo &TI = Context.getTargetInfo();
1940+
if (!TI.hasLongDoubleType() && UnqualTy == Context.LongDoubleTy) {
1941+
PartialDiagnostic PD = PDiag(diag::err_target_unsupported_type);
1942+
if (D)
1943+
PD << D;
1944+
else
1945+
PD << "expression";
1946+
1947+
if (Diag(Loc, PD, FD)
1948+
<< false /*show bit size*/ << 0 << Ty << false /*return*/
1949+
<< Context.getTargetInfo().getTriple().str()) {
1950+
if (D)
1951+
D->setInvalidDecl();
1952+
}
1953+
if (D)
1954+
targetDiag(D->getLocation(), diag::note_defined_here, FD) << D;
1955+
}
1956+
1957+
bool IsDouble = UnqualTy == Context.DoubleTy;
1958+
bool IsFloat = UnqualTy == Context.FloatTy;
1959+
if (IsRetTy && !TI.hasFPReturn() && (IsDouble || IsFloat)) {
1960+
PartialDiagnostic PD = PDiag(diag::err_target_unsupported_type);
1961+
if (D)
1962+
PD << D;
1963+
else
1964+
PD << "expression";
1965+
1966+
if (Diag(Loc, PD, FD)
1967+
<< false /*show bit size*/ << 0 << Ty << true /*return*/
19281968
<< Context.getTargetInfo().getTriple().str()) {
19291969
if (D)
19301970
D->setInvalidDecl();
@@ -1935,14 +1975,13 @@ void Sema::checkTypeSupport(QualType Ty, SourceLocation Loc, ValueDecl *D) {
19351975
};
19361976

19371977
CheckType(Ty);
1938-
19391978
if (const auto *FPTy = dyn_cast<FunctionProtoType>(Ty)) {
19401979
for (const auto &ParamTy : FPTy->param_types())
19411980
CheckType(ParamTy);
1942-
CheckType(FPTy->getReturnType());
1981+
CheckType(FPTy->getReturnType(), /*IsRetTy=*/true);
19431982
}
19441983
if (const auto *FNPTy = dyn_cast<FunctionNoProtoType>(Ty))
1945-
CheckType(FNPTy->getReturnType());
1984+
CheckType(FNPTy->getReturnType(), /*IsRetTy=*/true);
19461985
}
19471986

19481987
/// Looks through the macro-expansion chain for the given

clang/lib/Sema/SemaDecl.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9570,8 +9570,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
95709570
}
95719571
}
95729572

9573-
checkTypeSupport(NewFD->getType(), D.getBeginLoc(), NewFD);
9574-
95759573
if (!getLangOpts().CPlusPlus) {
95769574
// Perform semantic checking on the function declaration.
95779575
if (!NewFD->isInvalidDecl() && NewFD->isMain())
@@ -14857,6 +14855,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
1485714855
DeclsToCheckForDeferredDiags.insert(FD);
1485814856
}
1485914857

14858+
if (FD && !FD->isDeleted())
14859+
checkTypeSupport(FD->getType(), FD->getLocation(), FD);
14860+
1486014861
return dcl;
1486114862
}
1486214863

clang/test/Sema/x86-no-x87.cpp

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s -triple i686-linux-gnu -target-feature -x87 -DRET_ERROR
2+
// RUN: %clang_cc1 -fsyntax-only -verify %s -triple i686-linux-gnu -DNOERROR
3+
4+
#ifdef NOERROR
5+
// expected-no-diagnostics
6+
#endif
7+
8+
typedef long double long_double;
9+
10+
// Declaration is fine, unless it is called or defined.
11+
double decl(long_double x, long_double y);
12+
13+
template <typename T>
14+
T decl_ld_del(T);
15+
16+
// No code is generated for deleted functions
17+
long_double decl_ld_del(long_double) = delete;
18+
double decl_ld_del(double) = delete;
19+
float decl_ld_del(float) = delete;
20+
21+
#ifndef NOERROR
22+
// expected-error@+4{{'def' requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}}
23+
// expected-note@+3{{'def' defined here}}
24+
// expected-note@+2{{'x' defined here}}
25+
#endif
26+
int def(long_double x) {
27+
#ifndef NOERROR
28+
// expected-error@+2{{'x' requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}}
29+
#endif
30+
return (int)x;
31+
}
32+
33+
#ifndef NOERROR
34+
// expected-note@+3{{'ld_args' defined here}}
35+
// expected-note@+2{{'ld_args' defined here}}
36+
#endif
37+
int ld_args(long_double x, long_double y);
38+
39+
int call1(float x, float y) {
40+
#ifndef NOERROR
41+
// expected-error@+2 2{{'ld_args' requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}}
42+
#endif
43+
return ld_args(x, y);
44+
}
45+
46+
#ifndef NOERROR
47+
// expected-note@+2{{'ld_ret' defined here}}
48+
#endif
49+
long_double ld_ret(double x, double y);
50+
51+
int call2(float x, float y) {
52+
#ifndef NOERROR
53+
// expected-error@+2{{'ld_ret' requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}}
54+
#endif
55+
return (int)ld_ret(x, y);
56+
}
57+
58+
int binop(double x, double y) {
59+
#ifndef NOERROR
60+
// expected-error@+2 2{{expression requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}}
61+
#endif
62+
double z = (long_double)x * (long_double)y;
63+
return (int)z;
64+
}
65+
66+
void assign1(long_double *ret, double x) {
67+
#ifndef NOERROR
68+
// expected-error@+2{{expression requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}}
69+
#endif
70+
*ret = x;
71+
}
72+
73+
struct st_long_double1 {
74+
#ifndef NOERROR
75+
// expected-note@+2{{'ld' defined here}}
76+
#endif
77+
long_double ld;
78+
};
79+
80+
struct st_long_double2 {
81+
#ifndef NOERROR
82+
// expected-note@+2{{'ld' defined here}}
83+
#endif
84+
long_double ld;
85+
};
86+
87+
struct st_long_double3 {
88+
#ifndef NOERROR
89+
// expected-note@+2{{'ld' defined here}}
90+
#endif
91+
long_double ld;
92+
};
93+
94+
void assign2() {
95+
struct st_long_double1 st;
96+
#ifndef NOERROR
97+
// expected-error@+3{{expression requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}}
98+
// expected-error@+2{{'ld' requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}}
99+
#endif
100+
st.ld = 0.42;
101+
}
102+
103+
void assign3() {
104+
struct st_long_double2 st;
105+
#ifndef NOERROR
106+
// expected-error@+3{{expression requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}}
107+
// expected-error@+2{{'ld' requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}}
108+
#endif
109+
st.ld = 42;
110+
}
111+
112+
void assign4(double d) {
113+
struct st_long_double3 st;
114+
#ifndef NOERROR
115+
// expected-error@+3{{expression requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}}
116+
// expected-error@+2{{'ld' requires 'long_double' (aka 'long double') type support, but target 'i686-unknown-linux-gnu' does not support it}}
117+
#endif
118+
st.ld = d;
119+
}
120+
121+
void assign5() {
122+
// unused variable declaration is fine
123+
long_double ld = 0.42;
124+
}
125+
126+
#ifndef NOERROR
127+
// expected-note@+3{{'d_ret1' defined here}}
128+
// expected-error@+2{{'d_ret1' requires 'double' return type support, but target 'i686-unknown-linux-gnu' does not support it}}
129+
#endif
130+
double d_ret1(float x) {
131+
return 0.0;
132+
}
133+
134+
#ifndef NOERROR
135+
// expected-note@+2{{'d_ret2' defined here}}
136+
#endif
137+
double d_ret2(float x);
138+
139+
int d_ret3(float x) {
140+
#ifndef NOERROR
141+
// expected-error@+2{{'d_ret2' requires 'double' return type support, but target 'i686-unknown-linux-gnu' does not support it}}
142+
#endif
143+
return (int)d_ret2(x);
144+
}
145+
146+
#ifndef NOERROR
147+
// expected-note@+3{{'f_ret1' defined here}}
148+
// expected-error@+2{{'f_ret1' requires 'float' return type support, but target 'i686-unknown-linux-gnu' does not support it}}
149+
#endif
150+
float f_ret1(float x) {
151+
return 0.0f;
152+
}
153+
154+
#ifndef NOERROR
155+
// expected-note@+2{{'f_ret2' defined here}}
156+
#endif
157+
float f_ret2(float x);
158+
159+
int f_ret3(float x) {
160+
#ifndef NOERROR
161+
// expected-error@+2{{'f_ret2' requires 'float' return type support, but target 'i686-unknown-linux-gnu' does not support it}}
162+
#endif
163+
return (int)f_ret2(x);
164+
}

0 commit comments

Comments
 (0)