llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen
Author: Fangrui Song (MaskRay)
<details>
<summary>Changes</summary>
x86_64-w64-mingw32 uses the x87 format for `long double`, while
arm64ec-w64-mingw32 uses binary64, so data containing `long double`
cannot be shared with x64 code.
Accept -mlong-double-{64,80,128} for arm64ec-w64-mingw32. The default
remains binary64.
* -mlong-double-128 uses binary128, as AArch64 Linux does.
* -mlong-double-80 gives `long double` the x86-64 layout (size 16,
alignment 16) for struct layout and global initializers. As the
AArch64 backend does not support x86_fp80, report an error in CodeGen
when a `long double` value is used or appears in an emitted function
signature. Unused inline functions, e.g. in <cmath>, are not
diagnosed. `long double` is not an HFA base type, so a struct
containing it is passed in general-purpose registers.
LLM-aided
---
Full diff: https://github.com/llvm/llvm-project/pull/237827.diff
10 Files Affected:
- (modified) clang/include/clang/Basic/DiagnosticCommonKinds.td (+2)
- (modified) clang/lib/CodeGen/CGExprComplex.cpp (+6)
- (modified) clang/lib/CodeGen/CGExprScalar.cpp (+3)
- (modified) clang/lib/CodeGen/CodeGenModule.cpp (+27-1)
- (modified) clang/lib/CodeGen/CodeGenModule.h (+10)
- (modified) clang/lib/CodeGen/Targets/AArch64.cpp (+4-1)
- (modified) clang/lib/Driver/ToolChains/Clang.cpp (+3)
- (added) clang/test/CodeGen/arm64ec-long-double-error.cpp (+47)
- (added) clang/test/CodeGen/arm64ec-long-double.c (+41)
- (added) clang/test/Driver/arm64ec-long-double.c (+11)
``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td
b/clang/include/clang/Basic/DiagnosticCommonKinds.td
index 986acebce22e55..9161870a1c35fd 100644
--- a/clang/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td
@@ -369,6 +369,8 @@ def warn_target_unrecognized_env : Warning<
InGroup<InvalidCommandLineArgument>;
def err_target_unsupported_abi_with_fpu : Error<
"'%0' ABI is not supported with FPU">;
+def err_storage_only_long_double : Error<
+ "x87 'long double' values are not supported on target '%0'">;
def err_ppc_impossible_musttail: Error<
"'musttail' attribute for this call is impossible because %select{"
diff --git a/clang/lib/CodeGen/CGExprComplex.cpp
b/clang/lib/CodeGen/CGExprComplex.cpp
index 30d693fb371cb6..0d9d1751a2775c 100644
--- a/clang/lib/CodeGen/CGExprComplex.cpp
+++ b/clang/lib/CodeGen/CGExprComplex.cpp
@@ -94,6 +94,12 @@ class ComplexExprEmitter
ComplexPairTy Visit(Expr *E) {
ApplyDebugLocation DL(CGF, E);
+ if (CGF.CGM.hasStorageOnlyLongDouble() &&
+ CGF.CGM.diagnoseLongDoubleType(E->getType(), E->getExprLoc())) {
+ auto *Poison = llvm::PoisonValue::get(
+ CGF.ConvertType(CGF.getContext().LongDoubleTy));
+ return ComplexPairTy(Poison, Poison);
+ }
return StmtVisitor<ComplexExprEmitter, ComplexPairTy>::Visit(E);
}
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp
b/clang/lib/CodeGen/CGExprScalar.cpp
index 0e2e29f0745d27..28c4c642430ca6 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -498,6 +498,9 @@ class ScalarExprEmitter
Value *Visit(Expr *E) {
ApplyDebugLocation DL(CGF, E);
+ if (CGF.CGM.hasStorageOnlyLongDouble() &&
+ CGF.CGM.diagnoseLongDoubleType(E->getType(), E->getExprLoc()))
+ return llvm::PoisonValue::get(ConvertType(E->getType()));
return StmtVisitor<ScalarExprEmitter, Value*>::Visit(E);
}
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp
b/clang/lib/CodeGen/CodeGenModule.cpp
index ce084487a69820..a0719c318f6829 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -556,7 +556,10 @@ CodeGenModule::CodeGenModule(ASTContext &C,
Target(C.getTargetInfo()), ABI(createCXXABI(*this)),
VMContext(M.getContext()), VTables(*this), StackHandler(diags),
SanitizerMD(new SanitizerMetadata(*this)),
- AtomicOpts(Target.getAtomicOpts()) {
+ AtomicOpts(Target.getAtomicOpts()),
+ HasStorageOnlyLongDouble(&Target.getLongDoubleFormat() ==
+ &llvm::APFloat::x87DoubleExtended() &&
+ !Target.getTriple().isX86()) {
AbiMapper = std::make_unique<QualTypeMapper>(C, M.getDataLayout(), AbiAlloc);
AbiReverseMapper = std::make_unique<llvm::abi::IRTypeMapper>(
@@ -2964,10 +2967,33 @@ void
CodeGenModule::SetLLVMFunctionAttributes(GlobalDecl GD,
Error(Loc, "__vectorcall calling convention is not currently supported");
}
+ if (HasStorageOnlyLongDouble) {
+ SourceLocation Loc;
+ if (const Decl *D = GD.getDecl())
+ Loc = D->getLocation();
+ if (!diagnoseLongDoubleType(Info.getReturnType(), Loc))
+ for (const CGFunctionInfoArgInfo &Arg : Info.arguments())
+ if (diagnoseLongDoubleType(Arg.type, Loc))
+ break;
+ }
F->setAttributes(PAL);
F->setCallingConv(static_cast<llvm::CallingConv::ID>(CallingConv));
}
+bool CodeGenModule::diagnoseLongDoubleType(QualType Ty, SourceLocation Loc) {
+ if (const auto *AT = Ty->getAs<AtomicType>())
+ Ty = AT->getValueType();
+ if (const auto *CT = Ty->getAs<ComplexType>())
+ Ty = CT->getElementType();
+ else if (const auto *VT = Ty->getAs<VectorType>())
+ Ty = VT->getElementType();
+ if (!Ty->isSpecificBuiltinType(BuiltinType::LongDouble))
+ return false;
+ getDiags().Report(Loc, diag::err_storage_only_long_double)
+ << getTriple().str();
+ return true;
+}
+
static void removeImageAccessQualifier(std::string& TyName) {
std::string ReadOnlyQual("__read_only");
std::string::size_type ReadOnlyPos = TyName.find(ReadOnlyQual);
diff --git a/clang/lib/CodeGen/CodeGenModule.h
b/clang/lib/CodeGen/CodeGenModule.h
index e2bd6bc4701fda..9c9f95511cd13f 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -722,6 +722,10 @@ class CodeGenModule : public CodeGenTypeCache {
AtomicOptions AtomicOpts;
+ // 'long double' has the x87 format on a target that cannot compute with it,
+ // e.g. ARM64EC with -mlong-double-80; only its layout is supported.
+ bool HasStorageOnlyLongDouble;
+
// A set of functions which should be hot-patched; see
// -fms-hotpatch-functions-file (and -list). This will nearly always be
empty.
// The list is sorted for binary-searching.
@@ -748,6 +752,12 @@ class CodeGenModule : public CodeGenTypeCache {
/// Set the current Atomic options.
void setAtomicOpts(AtomicOptions AO) { AtomicOpts = AO; }
+ bool hasStorageOnlyLongDouble() const { return HasStorageOnlyLongDouble; }
+
+ /// If \p Ty contains a storage-only 'long double' value, report an error at
+ /// \p Loc and return true.
+ bool diagnoseLongDoubleType(QualType Ty, SourceLocation Loc);
+
/// Return true if we should emit location information for expressions.
bool getExpressionLocationsEnabled() const;
diff --git a/clang/lib/CodeGen/Targets/AArch64.cpp
b/clang/lib/CodeGen/Targets/AArch64.cpp
index e493f717af581d..85ca8888dbde0d 100644
--- a/clang/lib/CodeGen/Targets/AArch64.cpp
+++ b/clang/lib/CodeGen/Targets/AArch64.cpp
@@ -690,8 +690,11 @@ bool
AArch64ABIInfo::isHomogeneousAggregateBaseType(QualType Ty) const {
// but with the difference that any floating-point type is allowed,
// including __fp16.
if (const BuiltinType *BT = Ty->getAs<BuiltinType>()) {
+ // AArch64 cannot lower x86_fp80; pass an x87 'long double' in GPRs.
if (BT->isFloatingPoint())
- return true;
+ return BT->getKind() != BuiltinType::LongDouble ||
+ &getTarget().getLongDoubleFormat() !=
+ &llvm::APFloat::x87DoubleExtended();
} else if (const VectorType *VT = Ty->getAs<VectorType>()) {
if (auto Kind = VT->getVectorKind();
Kind == VectorKind::SveFixedLengthData ||
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp
b/clang/lib/Driver/ToolChains/Clang.cpp
index 61a81f33ab22dd..d0f04781338b6e 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6347,6 +6347,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction
&JA,
else if (TC.getTriple().isPPC() &&
(A->getOption().getID() != options::OPT_mlong_double_80))
A->render(Args, CmdArgs);
+ else if (TC.getTriple().isWindowsArm64EC() &&
+ TC.getTriple().isWindowsGNUEnvironment())
+ A->render(Args, CmdArgs);
else
D.Diag(diag::err_drv_unsupported_opt_for_target)
<< A->getAsString(Args) << TripleStr;
diff --git a/clang/test/CodeGen/arm64ec-long-double-error.cpp
b/clang/test/CodeGen/arm64ec-long-double-error.cpp
new file mode 100644
index 00000000000000..065de7a8fece0e
--- /dev/null
+++ b/clang/test/CodeGen/arm64ec-long-double-error.cpp
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -triple arm64ec-w64-windows-gnu -mlong-double-80 -std=c++17
-emit-llvm-only -verify=use %s
+// RUN: %clang_cc1 -triple arm64ec-w64-windows-gnu -mlong-double-80 -std=c++17
-emit-llvm-only -verify=ret -DRET %s
+// RUN: %clang_cc1 -triple arm64ec-w64-windows-gnu -mlong-double-80 -std=c++17
-emit-llvm-only -verify=param -DPARAM %s
+// RUN: %clang_cc1 -triple arm64ec-w64-windows-gnu -mlong-double-128
-std=c++17 -emit-llvm-only -verify=ld128 %s
+
+// ld128-no-diagnostics
+
+// Unused inline definitions, such as those in <cmath>, are not emitted.
+inline long double unused_fabs(long double x) { return __builtin_fabsl(x); }
+
+long double g = 1.5L;
+struct S { long double x; } gs = {2.5L};
+
+// Only the layout is needed here.
+static_assert(sizeof(long double) == 16 && alignof(long double) == 16);
+long double *addr() { return &g; }
+void copy(S *d) { *d = gs; }
+void pass(S s) {}
+
+#if RET
+// An address-taken declaration needs an ARM64EC exit thunk.
+// ret-error@+1 {{x87 'long double' values are not supported on target
'arm64ec-w64-windows-gnu'}}
+long double ret();
+void *take() { return (void *)&ret; }
+#elif PARAM
+// param-error@+1 {{x87 'long double' values are not supported}}
+void param(long double) {}
+#else
+typedef long double v2ld __attribute__((vector_size(32)));
+
+void use(double d, _Complex long double *c, _Atomic long double *a, v2ld *v) {
+ // use-error@+1 {{x87 'long double' values are not supported}}
+ d = g;
+ // use-error@+1 {{x87 'long double' values are not supported}}
+ gs.x = d;
+ // use-error@+1 2 {{x87 'long double' values are not supported}}
+ d = g < gs.x;
+ // use-error@+1 {{x87 'long double' values are not supported}}
+ d = __real__ *c;
+ // use-error@+1 {{x87 'long double' values are not supported}}
+ *a += 1;
+ // use-error@+1 {{x87 'long double' values are not supported}}
+ *v = *v + *v;
+ // use-error@+1 {{x87 'long double' values are not supported}}
+ unused_fabs(0);
+}
+#endif
diff --git a/clang/test/CodeGen/arm64ec-long-double.c
b/clang/test/CodeGen/arm64ec-long-double.c
new file mode 100644
index 00000000000000..85982c5024e308
--- /dev/null
+++ b/clang/test/CodeGen/arm64ec-long-double.c
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -triple arm64ec-w64-windows-gnu -emit-llvm -o - %s |
FileCheck %s --check-prefixes=CHECK,LD64
+// RUN: %clang_cc1 -triple arm64ec-w64-windows-gnu -mlong-double-80 -emit-llvm
-o - %s | FileCheck %s --check-prefixes=CHECK,LD80
+// RUN: %clang_cc1 -triple arm64ec-w64-windows-gnu -mlong-double-128
-emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,LD128
+
+// -mlong-double-80 gives 'long double' the layout of x86_64-w64-mingw32 so
that
+// data can be shared with x64 code, but values cannot be computed with.
+
+int ld_size = sizeof(long double);
+int ld_align = _Alignof(long double);
+// LD64: @ld_size = dso_local global i32 8
+// LD64: @ld_align = dso_local global i32 8
+// LD80: @ld_size = dso_local global i32 16
+// LD80: @ld_align = dso_local global i32 16
+// LD128: @ld_size = dso_local global i32 16
+// LD128: @ld_align = dso_local global i32 16
+
+// LD64: @g = dso_local global double 1.500000e+00, align 8
+// LD80: @g = dso_local global x86_fp80 1.500000e+00, align 16
+// LD128: @g = dso_local global fp128 1.500000e+00, align 16
+long double g = 1.5L;
+
+struct S {
+ long double x;
+};
+// LD64: @gs = dso_local global %struct.S { double 2.500000e+00 }, align 8
+// LD80: @gs = dso_local global %struct.S { x86_fp80 2.500000e+00 }, align 16
+// LD128: @gs = dso_local global %struct.S { fp128 2.500000e+00 }, align 16
+struct S gs = {2.5L};
+
+// The x87 format is not a homogeneous floating-point aggregate base type.
+// LD64: define {{.*}}void @pass_struct([1 x double] %s.coerce)
+// LD80: define {{.*}}void @pass_struct(i128 %s.coerce)
+// LD128: define {{.*}}void @pass_struct([1 x fp128] %s.coerce)
+void pass_struct(struct S s) {}
+
+// CHECK-LABEL: define {{.*}}void @copy(
+// LD80: call void @llvm.memcpy.p0.p0.i64(ptr align 16 %{{.*}}, ptr
align 16 %{{.*}}, i64 16, i1 false)
+void copy(struct S *d, struct S *s) { *d = *s; }
+
+// CHECK-LABEL: define {{.*}}ptr @addr(
+long double *addr(void) { return &g; }
diff --git a/clang/test/Driver/arm64ec-long-double.c
b/clang/test/Driver/arm64ec-long-double.c
new file mode 100644
index 00000000000000..3f30ff374deaf9
--- /dev/null
+++ b/clang/test/Driver/arm64ec-long-double.c
@@ -0,0 +1,11 @@
+// RUN: %clang -### --target=arm64ec-w64-mingw32 -mlong-double-64 -c %s 2>&1 |
FileCheck %s --check-prefix=LD64
+// RUN: %clang -### --target=arm64ec-w64-mingw32 -mlong-double-80 -c %s 2>&1 |
FileCheck %s --check-prefix=LD80
+// RUN: %clang -### --target=arm64ec-w64-mingw32 -mlong-double-128 -c %s 2>&1
| FileCheck %s --check-prefix=LD128
+// RUN: not %clang -### --target=arm64ec-pc-windows-msvc -mlong-double-80 -c
%s 2>&1 | FileCheck %s --check-prefix=MSVC
+// RUN: not %clang -### --target=aarch64-w64-mingw32 -mlong-double-80 -c %s
2>&1 | FileCheck %s --check-prefix=AARCH64
+
+// LD64: "-mlong-double-64"
+// LD80: "-mlong-double-80"
+// LD128: "-mlong-double-128"
+// MSVC: error: unsupported option '-mlong-double-80' for target
'arm64ec-pc-windows-msvc{{.*}}'
+// AARCH64: error: unsupported option '-mlong-double-80' for target
'aarch64-w64-windows-gnu'
``````````
</details>
https://github.com/llvm/llvm-project/pull/237827
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits