[libcxx-commits] [clang] [compiler-rt] [libcxx] Implement soft x87 `long double` for ARM64EC (PR #236406)
LIU Hao via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Oct 28 05:59:01 PDT 2026
https://github.com/lhmouse updated https://github.com/llvm/llvm-project/pull/236406
>From d16f92d05238b194cb43a6160f2bf56d65f5ee1b Mon Sep 17 00:00:00 2001
From: LIU Hao <lh_mouse at 126.com>
Date: Wed, 28 Oct 2026 13:45:53 +0800
Subject: [PATCH 1/7] Add `TargetInfo::hasSoftX87LongDouble()`
This will be true when we use `long double` for the x87 extended-precision
format for ARM64EC, and false for MSVC targets and others.
Assisted-by: DeepSeek V4.1 Flash
Signed-off-by: LIU Hao <lh_mouse at 126.com>
---
clang/include/clang/Basic/TargetInfo.h | 6 ++++++
clang/lib/Basic/Targets/AArch64.cpp | 15 +++++++++++++++
clang/lib/Basic/Targets/AArch64.h | 5 +++++
3 files changed, 26 insertions(+)
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index a6523e8eaaa437..32aeb5f645b3db 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -709,6 +709,12 @@ class TargetInfo : public TransferrableTargetInfo,
/// Whether half args and returns are supported.
virtual bool allowHalfArgsAndReturns() const { return HalfArgsAndReturns; }
+ /// Determine whether the target has the x87-specific `long double` type
+ /// without a physical x87. The target arm64ec-w64-mingw32 has this property.
+ /// On such a target, `long double` is an integer image and is not a true
+ /// floating-point type.
+ virtual bool hasSoftX87LongDouble() const { return false; }
+
/// Determine whether the __float128 type is supported on this target.
virtual bool hasFloat128Type() const { return HasFloat128; }
diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp
index 7ddf9d87ad163e..f9642c81ab7f4a 100644
--- a/clang/lib/Basic/Targets/AArch64.cpp
+++ b/clang/lib/Basic/Targets/AArch64.cpp
@@ -206,6 +206,10 @@ AArch64TargetInfo::AArch64TargetInfo(const llvm::Triple &Triple,
this->MCountName = "\01_mcount";
else if (Triple.getOS() == llvm::Triple::UnknownOS)
this->MCountName = Triple.isGNUEnvironment() ? "\01_mcount" : "mcount";
+
+ // This should be compatible with x86-64.
+ if (Triple.isWindowsArm64EC() && !Triple.isWindowsMSVCEnvironment())
+ HasFloat128 = true;
}
StringRef AArch64TargetInfo::getABI() const { return ABI; }
@@ -450,6 +454,11 @@ void AArch64TargetInfo::getTargetDefines(const LangOptions &Opts,
if (T.isLFI())
Builder.defineMacro("__LFI__");
+ if (HasFloat128) {
+ Builder.defineMacro("__FLOAT128__");
+ Builder.defineMacro("__SIZEOF_FLOAT128__", "16");
+ }
+
// Inline assembly supports AArch64 flag outputs.
Builder.defineMacro("__GCC_ASM_FLAG_OUTPUTS__");
@@ -1831,6 +1840,12 @@ WindowsARM64TargetInfo::WindowsARM64TargetInfo(const llvm::Triple &Triple,
SizeType = UnsignedLongLong;
PtrDiffType = SignedLongLong;
IntPtrType = SignedLongLong;
+
+ if (Triple.isWindowsArm64EC() && !Triple.isWindowsMSVCEnvironment()) {
+ // This should be compatible with x86_64-w64-mingw32.
+ LongDoubleWidth = LongDoubleAlign = SuitableAlign = 128;
+ LongDoubleFormat = &llvm::APFloat::x87DoubleExtended();
+ }
}
TargetInfo::BuiltinVaListKind
diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h
index 00e84cc4864399..c17a092265ddb1 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -295,6 +295,11 @@ class LLVM_LIBRARY_VISIBILITY WindowsARM64TargetInfo
BuiltinVaListKind getBuiltinVaListKind() const override;
CallingConvCheckResult checkCallingConvention(CallingConv CC) const override;
+
+ bool hasSoftX87LongDouble() const override {
+ return Triple.isWindowsArm64EC() &&
+ LongDoubleFormat == &llvm::APFloat::x87DoubleExtended();
+ }
};
// Windows ARM, MS (C++) ABI
>From 54a2f235a0e43373b16c27f52ffb89945bb39ef5 Mon Sep 17 00:00:00 2001
From: LIU Hao <lh_mouse at 126.com>
Date: Wed, 28 Oct 2026 14:01:02 +0800
Subject: [PATCH 2/7] Enable `-mlong-double-{64,80,128}` for ARM64EC non-MSVC
targets
Currently this means only arm64ec-w64-mingw32.
Assisted-by: DeepSeek V4.1 Flash
Signed-off-by: LIU Hao <lh_mouse at 126.com>
---
clang/lib/Driver/ToolChains/Clang.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 0893b481f4ce64..614843b01d3439 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6344,6 +6344,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().isWindowsMSVCEnvironment())
+ A->render(Args, CmdArgs);
else
D.Diag(diag::err_drv_unsupported_opt_for_target)
<< A->getAsString(Args) << TripleStr;
>From f182e56875789545eb8920019a9aca5c29647a61 Mon Sep 17 00:00:00 2001
From: LIU Hao <lh_mouse at 126.com>
Date: Wed, 28 Oct 2026 14:51:07 +0800
Subject: [PATCH 3/7] Define ABI for software x87 `long double`
Assisted-by: DeepSeek V4.1 Flash
Signed-off-by: LIU Hao <lh_mouse at 126.com>
---
clang/lib/CodeGen/Targets/X86.cpp | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index a6789e924dc6f5..a512e590751ba4 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -3570,6 +3570,12 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, unsigned &FreeSSERegs,
if (Ty->isVoidType())
return ABIArgInfo::getIgnore();
+ // The 80-bit `long double` is passed and returned in memory.
+ if (getTarget().hasSoftX87LongDouble() &&
+ Ty->isSpecificBuiltinType(BuiltinType::LongDouble))
+ return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
+ /*ByVal=*/false);
+
bool PromoteScopedEnum = false;
if (const auto *ED = Ty->getAsEnumDecl()) {
Ty = ED->getIntegerType();
>From c868b1e8475b6dda35ecaf9603a9f4c55ba60357 Mon Sep 17 00:00:00 2001
From: LIU Hao <lh_mouse at 126.com>
Date: Wed, 28 Oct 2026 14:45:29 +0800
Subject: [PATCH 4/7] Implement conversion routines between `long double` and
`double`
These are:
* `double __arm64ec_load_x87_long_double(const long double* image)`
* `void __arm64ec_store_x87_long_double(long double* image, double value)`
The functions match physical x87, tested on Intel i9-14900K.
Assisted-by: DeepSeek V4.1 Flash
Assisted-by: Claude Opus 5.5
Signed-off-by: LIU Hao <lh_mouse at 126.com>
---
compiler-rt/lib/builtins/CMakeLists.txt | 2 +-
.../lib/builtins/arm64ec/x87_long_double.c | 187 ++++++++++++++++++
2 files changed, 188 insertions(+), 1 deletion(-)
create mode 100644 compiler-rt/lib/builtins/arm64ec/x87_long_double.c
diff --git a/compiler-rt/lib/builtins/CMakeLists.txt b/compiler-rt/lib/builtins/CMakeLists.txt
index 67427a1f4b8a49..9e10e477f4647e 100644
--- a/compiler-rt/lib/builtins/CMakeLists.txt
+++ b/compiler-rt/lib/builtins/CMakeLists.txt
@@ -898,7 +898,7 @@ set(armv7k_SOURCES ${arm_SOURCES})
set(arm64_SOURCES ${aarch64_SOURCES})
set(arm64e_SOURCES ${aarch64_SOURCES})
set(arm64_32_SOURCES ${aarch64_SOURCES})
-set(arm64ec_SOURCES ${aarch64_SOURCES})
+set(arm64ec_SOURCES ${aarch64_SOURCES} arm64ec/x87_long_double.c)
set(aarch64_lfi_SOURCES ${aarch64_SOURCES})
set(aarch64_be_SOURCES ${aarch64_SOURCES})
diff --git a/compiler-rt/lib/builtins/arm64ec/x87_long_double.c b/compiler-rt/lib/builtins/arm64ec/x87_long_double.c
new file mode 100644
index 00000000000000..c9c77d996432cb
--- /dev/null
+++ b/compiler-rt/lib/builtins/arm64ec/x87_long_double.c
@@ -0,0 +1,187 @@
+//===-- x87_long_double.c - ARM64EC soft x87 `long double` helpers --------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// ARM64EC is ABI compatible with x86-64 Windows, where `long double` is the
+// x87 80-bit extended format held in a 16-byte, 16-byte-aligned slot. There
+// is no x87 FPU on ARM64, so the compiler keeps such values in memory and
+// copies them around with integer instructions only; whenever a computation
+// has to be carried out, it converts the value to `double` with
+// `__arm64ec_load_x87_long_double()`, computes in `double`, and converts the
+// result back with `__arm64ec_store_x87_long_double()`.
+//
+// The 16-byte slot (little endian, as used by both x86-64 and ARM64) is
+//
+// offset 0: 64-bit significand, including the explicit integer bit
+// offset 8: 16-bit sign/exponent word: bit 15 is the sign, bits 14..0 is
+// the biased exponent
+// offset 10: 6 bytes of padding that are not read or written by the x87
+//
+// Note that the x87 extended format does not use a hidden bit: The
+// significand of a normal number has bit 63 set, and for a denormal or zero
+// bit 63 is clear.
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdint.h>
+
+/// The memory image of an x87 extended-precision value. Deliberately declared
+/// as integers so that neither the compiler nor this file can end up using
+/// floating-point instructions on it.
+typedef struct {
+ uint64_t significand;
+ uint16_t sign_exponent;
+ uint16_t padding[3];
+} arm64ec_x87_image;
+
+typedef union {
+ uint64_t u;
+ double d;
+} arm64ec_double_bits;
+
+#define ARM64EC_X87_EXP_BIAS 16383
+#define ARM64EC_X87_EXP_MAX 0x7fffu
+#define ARM64EC_DBL_EXP_MAX 0x7ffu
+
+/// The x87 real indefinite, which is what it produces for an invalid operand.
+/// Its sign bit is always set.
+static double arm64ec_indefinite_qnan(void) {
+ arm64ec_double_bits v;
+ v.u = 0xfff8000000000000ull;
+ return v.d;
+}
+
+static double arm64ec_make_double(unsigned sign, uint32_t biased_exponent,
+ uint64_t fraction) {
+ arm64ec_double_bits v;
+ v.u = ((uint64_t)sign << 63) | ((uint64_t)biased_exponent << 52) | fraction;
+ return v.d;
+}
+
+/// Convert an x87 extended-precision memory image to `double`, rounding to
+/// nearest with ties to even. This matches what the x87 itself does for
+/// 'fld m80; fstp m64' with the precision control set to 64 bits.
+double __arm64ec_load_x87_long_double(const arm64ec_x87_image *image) {
+ uint64_t m = image->significand;
+ unsigned sign = (unsigned)(image->sign_exponent >> 15);
+ unsigned e = (unsigned)(image->sign_exponent & 0x7fff);
+ int exponent;
+ uint64_t kept, dropped;
+
+ if (e == 0x7fff) {
+ if (m == 0x8000000000000000ull)
+ // Infinity.
+ return arm64ec_make_double(sign, ARM64EC_DBL_EXP_MAX, 0);
+ // A NaN whose integer bit is set is a real NaN: keep the top 52 bits of
+ // its payload and make sure the quiet bit is set. Anything else (a
+ // pseudo-NaN or pseudo-infinity with the integer bit clear) is an invalid
+ // encoding, for which the hardware produces the indefinite QNaN.
+ if ((m >> 63) == 0)
+ return arm64ec_indefinite_qnan();
+ return arm64ec_make_double(sign, ARM64EC_DBL_EXP_MAX,
+ ((m >> 11) & 0xfffffffffffffull) |
+ 0x8000000000000ull);
+ }
+
+ // Non-zero biased exponent with the integer bit clear is invalid.
+ if (e != 0 && (m >> 63) == 0)
+ return arm64ec_indefinite_qnan();
+
+ // A pseudo-denormal (biased exponent zero with the integer bit set) is a
+ // perfectly valid representation of m * 2^-16445 and behaves like the
+ // normal number with biased exponent one. A denormalized number is far
+ // below the range of `double` and therefore underflows to zero.
+ if (e == 0) {
+ e = (unsigned)(m >> 63);
+ if (e == 0) // zero or denormal, both of which become a signed zero
+ return arm64ec_make_double(sign, 0, 0);
+ }
+
+ // value == m * 2^(exponent - 63), with bit 63 of m set
+ exponent = (int)e - ARM64EC_X87_EXP_BIAS;
+
+ if (exponent > 1023)
+ return arm64ec_make_double(sign, ARM64EC_DBL_EXP_MAX, 0);
+
+ if (exponent >= -1022) {
+ // A normal `double`: round the 64-bit significand to 53 bits.
+ kept = m >> 11;
+ dropped = m & 0x7ffu;
+ if (dropped > 0x400u || (dropped == 0x400u && (kept & 1)))
+ if (++kept == 0x20000000000000ull) {
+ kept >>= 1;
+ if (++exponent > 1023)
+ return arm64ec_make_double(sign, ARM64EC_DBL_EXP_MAX, 0);
+ }
+ return arm64ec_make_double(sign, (uint32_t)(exponent + 1023),
+ kept & 0xfffffffffffffull);
+ }
+
+ // A subnormal `double`: the result is
+ // round(m * 2^(exponent + 1011)) units of 2^-1074.
+ int shift = -(exponent + 1011);
+ uint64_t k;
+
+ if (shift > 64)
+ k = 0;
+ else if (shift == 64) {
+ // m is in [2^63, 2^64), so the quotient is in [0.5, 1) and only a value
+ // strictly greater than one half rounds up.
+ k = m > 0x8000000000000000ull ? 1 : 0;
+ } else {
+ uint64_t quotient = m >> shift;
+ uint64_t remainder = m & ((1ull << shift) - 1);
+ uint64_t half = 1ull << (shift - 1);
+ if (remainder > half || (remainder == half && (quotient & 1)))
+ ++quotient;
+ k = quotient;
+ }
+ // 'k' may reach 2^52, which is exactly the smallest normal `double`; it
+ // then carries into the exponent field on its own.
+ return arm64ec_make_double(sign, 0, k);
+}
+
+/// Convert a `double` to an x87 extended-precision memory image. Every
+/// `double` is exactly representable in the x87 format, so this never rounds.
+/// Padding bits are undefined.
+void __arm64ec_store_x87_long_double(arm64ec_x87_image *image, double value) {
+ arm64ec_double_bits v;
+ unsigned sign;
+ unsigned e;
+ uint64_t m;
+
+ v.d = value;
+ sign = (unsigned)(v.u >> 63);
+ e = (unsigned)((v.u >> 52) & 0x7ff);
+ m = v.u & 0xfffffffffffffull;
+
+ if (e == ARM64EC_DBL_EXP_MAX) {
+ if (m == 0)
+ image->significand = 0x8000000000000000ull; // infinity
+ else
+ // Map the 52-bit `double` payload onto the top bits of the 63-bit x87
+ // fraction. Like 'fld m64', quiet a signalling NaN.
+ image->significand = (m << 11) | 0xc000000000000000ull;
+ image->sign_exponent = (uint16_t)((sign << 15) | ARM64EC_X87_EXP_MAX);
+ } else if (e == 0) {
+ if (m == 0) {
+ image->significand = 0; // zero
+ image->sign_exponent = (uint16_t)(sign << 15);
+ } else {
+ // A subnormal `double` has to be normalized: it is
+ // 1.f * 2^(p - 1074) where p is the index of its top set bit.
+ int p = 63 - __builtin_clzll(m);
+ image->significand = m << (63 - p);
+ image->sign_exponent = (uint16_t)((sign << 15) | (unsigned)(p + 15309));
+ }
+ } else {
+ // Shift the 52-bit `double` fraction up to the top of the 63-bit x87
+ // fraction and add the explicit integer bit.
+ image->significand = (m << 11) | 0x8000000000000000ull;
+ image->sign_exponent = (uint16_t)((sign << 15) | (e + 15360));
+ }
+}
>From dea9109659a02fe3e270ea142650dbd95299c47f Mon Sep 17 00:00:00 2001
From: LIU Hao <lh_mouse at 126.com>
Date: Wed, 28 Oct 2026 15:37:59 +0800
Subject: [PATCH 5/7] Implement code generation of software x87 `long double`
Assisted-by: DeepSeek V4.1 Flash
Signed-off-by: LIU Hao <lh_mouse at 126.com>
---
clang/lib/CodeGen/CGBuiltin.cpp | 320 +++++++++++++++++++++++++++
clang/lib/CodeGen/CGExprComplex.cpp | 81 ++++++-
clang/lib/CodeGen/CGExprConstant.cpp | 39 +++-
clang/lib/CodeGen/CGExprScalar.cpp | 159 ++++++++++++-
clang/lib/CodeGen/CodeGenFunction.h | 12 +
clang/lib/CodeGen/CodeGenModule.h | 4 +
clang/lib/CodeGen/CodeGenTypes.cpp | 7 +
7 files changed, 611 insertions(+), 11 deletions(-)
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index e154b9e752fd5c..cc10e4e428fdbf 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -2714,12 +2714,332 @@ static void ClearPadding(CodeGenFunction &CGF, Address Src,
}
}
+/// Lower builtins that operate on ARM64EC's software x87 `long double`. The
+/// ones that are required to be exact (fabs, copysign, signbit, isnan, isinf,
+/// isfinite, isnormal, fpclassify) are implemented with integer operations on
+/// the image itself; everything else is evaluated in `double` exactly like the
+/// C library does for its own long double entry points. Returns false when the
+/// builtin does not involve such a value.
+bool CodeGenFunction::EmitSoftX87LongDoubleBuiltin(unsigned BuiltinID,
+ const CallExpr *E,
+ ReturnValueSlot ReturnValue,
+ RValue &Result) {
+ ASTContext &Ctx = getContext();
+ // Note: BuiltinInfo::getName() returns a std::string by value.
+ const std::string Name = Ctx.BuiltinInfo.getName(BuiltinID);
+
+ // Everything below rewrites a call into its binary64 equivalent, which only
+ // makes sense when a `long double` is *consumed*. The 'strtold' family takes
+ // a string instead and produces a `long double`, so it must be called with
+ // its own signature; it is handled by the C library in 80-bit precision.
+ bool ConsumesLongDouble = false;
+ for (const Expr *Arg : E->arguments())
+ ConsumesLongDouble |=
+ Arg->getType()->isSpecificBuiltinType(BuiltinType::LongDouble);
+ // The 'nan' and 'inf' families take a string or no argument at all and still
+ // produce a `long double`, so they do not consume one but do need the integer
+ // image constructed below: they are the only builtins that yield a constant.
+ const bool YieldsConstant =
+ Name == "nanl" || Name == "__builtin_nanl" || Name == "nansl" ||
+ Name == "__builtin_nansl" || Name == "infl" || Name == "__builtin_infl" ||
+ Name == "huge_vall" || Name == "__builtin_huge_vall";
+ if (!ConsumesLongDouble && !YieldsConstant)
+ return false;
+ llvm::Type *I128Ty = llvm::IntegerType::get(getLLVMContext(), 128);
+ llvm::Type *I64Ty = Builder.getInt64Ty();
+ llvm::Type *I32Ty = Builder.getInt32Ty();
+ llvm::Type *I16Ty = Builder.getInt16Ty();
+ SourceLocation Loc = E->getExprLoc();
+
+ llvm::Constant *SignMask =
+ llvm::ConstantInt::get(I128Ty, llvm::APInt::getOneBitSet(128, 79));
+ llvm::Constant *MagnitudeMask =
+ llvm::ConstantInt::get(I128Ty, ~llvm::APInt::getOneBitSet(128, 79));
+
+ // Extract the 15-bit exponent field and the significand of an image.
+ auto ExpField = [&](llvm::Value *V) -> llvm::Value * {
+ llvm::Value *Wide = Builder.CreateLShr(V, 64);
+ return Builder.CreateAnd(Builder.CreateTrunc(Wide, I16Ty),
+ Builder.getInt16(0x7FFF));
+ };
+ auto MantissaOf = [&](llvm::Value *V) -> llvm::Value * {
+ return Builder.CreateTrunc(V, I64Ty);
+ };
+ // The sign lives in bit 15 of the sign/exponent word, i.e. in bit 79 of the
+ // image; the six padding bytes above it must not leak into the result.
+ auto SignBitOf = [&](llvm::Value *V) -> llvm::Value * {
+ return Builder.CreateAnd(
+ Builder.CreateTrunc(Builder.CreateLShr(V, 79), I32Ty),
+ Builder.getInt32(1));
+ };
+ auto IsInfImage = [&](llvm::Value *V) -> llvm::Value * {
+ llvm::Value *ExpAllOnes =
+ Builder.CreateICmpEQ(ExpField(V), Builder.getInt16(0x7FFF));
+ llvm::Value *FracZero = Builder.CreateICmpEQ(
+ MantissaOf(V), Builder.getInt64(0x8000000000000000ULL));
+ return Builder.CreateAnd(ExpAllOnes, FracZero);
+ };
+ auto IsNaNAllOnes = [&](llvm::Value *V) -> llvm::Value * {
+ llvm::Value *ExpAllOnes =
+ Builder.CreateICmpEQ(ExpField(V), Builder.getInt16(0x7FFF));
+ llvm::Value *FracZero = Builder.CreateICmpEQ(
+ MantissaOf(V), Builder.getInt64(0x8000000000000000ULL));
+ return Builder.CreateAnd(ExpAllOnes, Builder.CreateNot(FracZero));
+ };
+
+ // Note that BuiltinInfo::getName() returns the C library spelling for the
+ // LibBuiltin entries, i.e. "fabsl" rather than "__builtin_fabsl", and the
+ // "__builtin_..." spelling for the ones that only exist as builtins.
+ if (Name == "fabsl" || Name == "__builtin_fabsl") {
+ if (!E->getType()->isSpecificBuiltinType(BuiltinType::LongDouble))
+ return false;
+ Result = RValue::get(
+ Builder.CreateAnd(EmitScalarExpr(E->getArg(0)), MagnitudeMask));
+ return true;
+ }
+
+ if (Name == "copysignl" || Name == "__builtin_copysignl") {
+ if (!E->getType()->isSpecificBuiltinType(BuiltinType::LongDouble))
+ return false;
+ llvm::Value *X = EmitScalarExpr(E->getArg(0));
+ llvm::Value *Y = EmitScalarExpr(E->getArg(1));
+ llvm::Value *Magnitude = Builder.CreateAnd(X, MagnitudeMask);
+ llvm::Value *Sign = Builder.CreateAnd(Y, SignMask);
+ Result = RValue::get(Builder.CreateOr(Magnitude, Sign));
+ return true;
+ }
+
+ if (Name == "__builtin_signbit") {
+ if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+ BuiltinType::LongDouble))
+ return false;
+ llvm::Value *V = EmitScalarExpr(E->getArg(0));
+ Result = RValue::get(SignBitOf(V));
+ return true;
+ }
+
+ if (Name == "__builtin_isnan") {
+ if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+ BuiltinType::LongDouble))
+ return false;
+ llvm::Value *V = EmitScalarExpr(E->getArg(0));
+ llvm::Value *ExpAllOnes =
+ Builder.CreateICmpEQ(ExpField(V), Builder.getInt16(0x7FFF));
+ llvm::Value *IntBitZero = Builder.CreateICmpEQ(
+ Builder.CreateLShr(MantissaOf(V), 63), Builder.getInt64(0));
+ Result = RValue::get(Builder.CreateZExt(
+ Builder.CreateOr(IsNaNAllOnes(V),
+ Builder.CreateAnd(ExpAllOnes, IntBitZero)),
+ I32Ty));
+ return true;
+ }
+
+ if (Name == "__builtin_isinf" || Name == "__builtin_isinf_sign") {
+ if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+ BuiltinType::LongDouble))
+ return false;
+ llvm::Value *V = EmitScalarExpr(E->getArg(0));
+ llvm::Value *IsInf = IsInfImage(V);
+ if (Name == "__builtin_isinf") {
+ Result = RValue::get(Builder.CreateZExt(IsInf, I32Ty));
+ } else {
+ llvm::Value *Negative =
+ Builder.CreateICmpNE(SignBitOf(V), Builder.getInt32(0));
+ llvm::Value *Signed = Builder.CreateSelect(
+ Negative, Builder.CreateSub(Builder.getInt32(0), Builder.getInt32(1)),
+ Builder.getInt32(1));
+ Result =
+ RValue::get(Builder.CreateSelect(IsInf, Signed, Builder.getInt32(0)));
+ }
+ return true;
+ }
+
+ if (Name == "__builtin_isfinite") {
+ if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+ BuiltinType::LongDouble))
+ return false;
+ llvm::Value *V = EmitScalarExpr(E->getArg(0));
+ llvm::Value *Exp = ExpField(V);
+ llvm::Value *ExpAllOnes =
+ Builder.CreateICmpEQ(Exp, Builder.getInt16(0x7FFF));
+ llvm::Value *ExpZero = Builder.CreateICmpEQ(Exp, Builder.getInt16(0));
+ llvm::Value *IntBitZero = Builder.CreateICmpEQ(
+ Builder.CreateLShr(MantissaOf(V), 63), Builder.getInt64(0));
+ llvm::Value *Unnormal = Builder.CreateAnd(
+ Builder.CreateNot(ExpAllOnes),
+ Builder.CreateAnd(Builder.CreateNot(ExpZero), IntBitZero));
+ Result = RValue::get(
+ Builder.CreateZExt(Builder.CreateAnd(Builder.CreateNot(ExpAllOnes),
+ Builder.CreateNot(Unnormal)),
+ I32Ty));
+ return true;
+ }
+
+ if (Name == "__builtin_isnormal") {
+ if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+ BuiltinType::LongDouble))
+ return false;
+ llvm::Value *V = EmitScalarExpr(E->getArg(0));
+ llvm::Value *Exp = ExpField(V);
+ llvm::Value *Normal =
+ Builder.CreateAnd(Builder.CreateICmpNE(Exp, Builder.getInt16(0)),
+ Builder.CreateICmpNE(Exp, Builder.getInt16(0x7FFF)));
+ Normal = Builder.CreateAnd(
+ Normal, Builder.CreateICmpNE(Builder.CreateLShr(MantissaOf(V), 63),
+ Builder.getInt64(0)));
+ Result = RValue::get(Builder.CreateZExt(Normal, I32Ty));
+ return true;
+ }
+
+ if (Name == "__builtin_fpclassify") {
+ if (!E->getArg(5)->getType()->isSpecificBuiltinType(
+ BuiltinType::LongDouble))
+ return false;
+ llvm::Value *V = EmitScalarExpr(E->getArg(5));
+ llvm::Value *Exp = ExpField(V);
+ llvm::Value *Man = MantissaOf(V);
+ llvm::Value *ExpAllOnes =
+ Builder.CreateICmpEQ(Exp, Builder.getInt16(0x7FFF));
+ llvm::Value *ExpZero = Builder.CreateICmpEQ(Exp, Builder.getInt16(0));
+ llvm::Value *ManZero = Builder.CreateICmpEQ(Man, Builder.getInt64(0));
+ llvm::Value *IntBitZero =
+ Builder.CreateICmpEQ(Builder.CreateLShr(Man, 63), Builder.getInt64(0));
+ llvm::Value *IsNaN = Builder.CreateOr(
+ Builder.CreateAnd(ExpAllOnes, Builder.CreateNot(ManZero)),
+ Builder.CreateAnd(
+ Builder.CreateNot(ExpAllOnes),
+ Builder.CreateAnd(Builder.CreateNot(ExpZero), IntBitZero)));
+ llvm::Value *IsInf = Builder.CreateAnd(ExpAllOnes, ManZero);
+ llvm::Value *IsZero = Builder.CreateAnd(ExpZero, ManZero);
+ llvm::Value *IsSub = Builder.CreateAnd(
+ ExpZero, Builder.CreateAnd(Builder.CreateNot(ManZero), IntBitZero));
+ llvm::Value *IsNormal = Builder.CreateNot(Builder.CreateOr(
+ IsNaN, Builder.CreateOr(IsInf, Builder.CreateOr(IsZero, IsSub))));
+ llvm::Value *A0 = EmitScalarExpr(E->getArg(0));
+ llvm::Value *A1 = EmitScalarExpr(E->getArg(1));
+ llvm::Value *A2 = EmitScalarExpr(E->getArg(2));
+ llvm::Value *A3 = EmitScalarExpr(E->getArg(3));
+ llvm::Value *A4 = EmitScalarExpr(E->getArg(4));
+ llvm::Value *R = A4; // zero
+ R = Builder.CreateSelect(IsSub, A3, R);
+ R = Builder.CreateSelect(IsNormal, A2, R);
+ R = Builder.CreateSelect(IsInf, A1, R);
+ R = Builder.CreateSelect(IsNaN, A0, R);
+ Result = RValue::get(R);
+ return true;
+ }
+
+ if (Name == "infl" || Name == "__builtin_infl" || Name == "huge_vall" ||
+ Name == "__builtin_huge_vall") {
+ // +-infinity is significand 0x8000000000000000 with sign/exponent 0x7FFF,
+ // in a 16-byte slot whose six padding bytes are zero.
+ llvm::APInt Image(128, 0x8000000000000000ull);
+ Image |= llvm::APInt(128, 0x7fffu) << 64;
+ Result = RValue::get(llvm::ConstantInt::get(I128Ty, Image));
+ return true;
+ }
+
+ if (Name == "nanl" || Name == "__builtin_nanl" || Name == "nansl" ||
+ Name == "__builtin_nansl") {
+ // The quiet NaN has significand 0xC000000000000000 and the signalling one
+ // has 0xA000000000000000, both with sign/exponent 0x7FFF; the payload of
+ // the tag argument is not propagated.
+ const bool Signalling = Name == "nansl" || Name == "__builtin_nansl";
+ llvm::APInt Image(128, Signalling ? 0xa000000000000000ull
+ : 0xc000000000000000ull);
+ Image |= llvm::APInt(128, 0x7fffu) << 64;
+ Result = RValue::get(llvm::ConstantInt::get(I128Ty, Image));
+ return true;
+ }
+
+ // Everything else is evaluated in `double` through the binary64 entry point
+ // of the same name, which is what the C library does internally as well.
+ // A handful of them are required not to lose a single bit, though: those are
+ // called through their own `long double` C library function, which works on
+ // the 80-bit image directly.
+ // Variadic builtins (the `printf` and `scanf` families) must be forwarded to
+ // the C library with their ellipsis arguments untouched: the x64 calling
+ // convention, which ARM64EC reproduces, passes a 16-byte `long double` by
+ // reference, so the value may not be promoted to `double` here.
+ ASTContext::GetBuiltinTypeError TypeError;
+ if (const FunctionProtoType *BPT =
+ Ctx.GetBuiltinType(BuiltinID, TypeError)->getAs<FunctionProtoType>();
+ BPT && BPT->isVariadic())
+ return false;
+
+ // (`ilogbl` and `logbl` are not among them: ucrtbase is the only provider on
+ // this target, and it takes and returns binary64.)
+ //
+ // The `nexttoward` family is special: its whole purpose is to step to the
+ // adjacent representable value, so evaluating it in binary64 would return a
+ // value that is not adjacent at all. `nexttowardl` is an alias of
+ // `nextafterl` in the C library and both walk the 80-bit image directly.
+ std::string LibName = Name;
+ const StringRef BuiltinPrefix = "__builtin_";
+ if (StringRef(LibName).starts_with(BuiltinPrefix))
+ LibName.erase(0, BuiltinPrefix.size());
+
+ const bool Exact = LibName == "frexpl" || LibName == "ldexpl" ||
+ LibName == "modfl" || LibName == "nextafterl" ||
+ LibName == "nexttowardl" || LibName == "nexttoward" ||
+ LibName == "nexttowardf";
+ if (!Exact && LibName.size() > 1 && LibName.back() == 'l')
+ LibName.pop_back();
+
+ SmallVector<llvm::Type *, 4> ArgTys;
+ SmallVector<llvm::Value *, 4> Args;
+ for (unsigned I = 0, N = E->getNumArgs(); I != N; ++I) {
+ llvm::Value *A = EmitScalarExpr(E->getArg(I));
+ if (!Exact &&
+ E->getArg(I)->getType()->isSpecificBuiltinType(BuiltinType::LongDouble))
+ A = EmitSoftX87LdToDouble(A, Loc);
+ Args.push_back(A);
+ ArgTys.push_back(A->getType());
+ }
+
+ const bool LongDoubleResult =
+ E->getType()->isSpecificBuiltinType(BuiltinType::LongDouble);
+ llvm::Type *RetTy = (!LongDoubleResult || Exact) ? ConvertType(E->getType())
+ : Builder.getDoubleTy();
+ llvm::FunctionType *FT = llvm::FunctionType::get(RetTy, ArgTys, false);
+ llvm::FunctionCallee Fn = CGM.CreateRuntimeFunction(FT, LibName);
+ llvm::Value *Call = Builder.CreateCall(Fn, Args);
+ if (LongDoubleResult && !Exact)
+ Call = EmitDoubleToSoftX87Ld(Call, Loc);
+ Result = RValue::get(Call);
+ return true;
+}
+
RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
const CallExpr *E,
ReturnValueSlot ReturnValue) {
assert(!getContext().BuiltinInfo.isImmediate(BuiltinID) &&
"Should not codegen for consteval builtins");
+ // ARM64EC: `long double` is an x87 image kept in a 16-byte integer slot and
+ // has no hardware arithmetic. Builtins that take or return such a value must
+ // therefore never be lowered to FP intrinsics.
+ //
+ // The C++ standard library counterparts of the C library functions
+ // (`std::move`, `std::forward` and friends) are builtins as well, but they
+ // are lowered to a plain address computation and never touch the value, so
+ // they are none of our business.
+ const FunctionDecl *DirectCallee = E->getDirectCallee();
+ const bool IsCxxLibBuiltin =
+ DirectCallee && DirectCallee->getDeclContext()->isNamespace();
+ if (getTarget().hasSoftX87LongDouble() && !IsCxxLibBuiltin) {
+ bool TouchesLongDouble =
+ E->getType()->isSpecificBuiltinType(BuiltinType::LongDouble);
+ for (const Expr *Arg : E->arguments())
+ TouchesLongDouble |=
+ Arg->getType()->isSpecificBuiltinType(BuiltinType::LongDouble);
+ if (TouchesLongDouble) {
+ RValue X87Result;
+ if (EmitSoftX87LongDoubleBuiltin(BuiltinID, E, ReturnValue, X87Result))
+ return X87Result;
+ }
+ }
+
const FunctionDecl *FD = GD.getDecl()->getAsFunction();
// See if we can constant fold this builtin. If so, don't emit it at all.
// TODO: Extend this handling to all builtin calls that we can constant-fold.
diff --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp
index 30d693fb371cb6..8d1330ee29e81b 100644
--- a/clang/lib/CodeGen/CGExprComplex.cpp
+++ b/clang/lib/CodeGen/CGExprComplex.cpp
@@ -276,6 +276,19 @@ class ComplexExprEmitter
ComplexPairTy EmitBinSub(const BinOpInfo &Op);
ComplexPairTy EmitBinMul(const BinOpInfo &Op);
ComplexPairTy EmitBinDiv(const BinOpInfo &Op);
+
+ /// ARM64EC: `long double` in the x87 80-bit format is represented by a
+ /// 128-bit integer, so `long double _Complex` would otherwise be emitted as
+ /// *integer* complex arithmetic, which neither implements x87 semantics nor
+ /// even accepts a real operand next to a complex one. Report whether `Op`
+ /// is such an operation.
+ bool isSoftX87ComplexOp(const BinOpInfo &Op) const {
+ return CGF.getTarget().hasSoftX87LongDouble() &&
+ !Op.LHS.first->getType()->isFloatingPointTy();
+ }
+ /// Rewrite the components of a soft-x87 `BinOpInfo` as `double`.
+ BinOpInfo softX87ComplexToDouble(const BinOpInfo &Op);
+
ComplexPairTy EmitAlgebraicDiv(llvm::Value *A, llvm::Value *B, llvm::Value *C,
llvm::Value *D);
ComplexPairTy EmitRangeReductionDiv(llvm::Value *A, llvm::Value *B,
@@ -695,6 +708,16 @@ ComplexPairTy ComplexExprEmitter::VisitMinus(const UnaryOperator *E,
if (Op.first->getType()->isFloatingPointTy()) {
ResR = Builder.CreateFNeg(Op.first, "neg.r");
ResI = Builder.CreateFNeg(Op.second, "neg.i");
+ } else if (CGF.getTarget().hasSoftX87LongDouble()) {
+ // ARM64EC: the components are x87 images held in integers, so negate them
+ // through `double` rather than with a two's complement negation.
+ SourceLocation Loc;
+ ResR = CGF.EmitDoubleToSoftX87Ld(
+ Builder.CreateFNeg(CGF.EmitSoftX87LdToDouble(Op.first, Loc), "neg.r"),
+ Loc);
+ ResI = CGF.EmitDoubleToSoftX87Ld(
+ Builder.CreateFNeg(CGF.EmitSoftX87LdToDouble(Op.second, Loc), "neg.i"),
+ Loc);
} else {
ResR = Builder.CreateNeg(Op.first, "neg.r");
ResI = Builder.CreateNeg(Op.second, "neg.i");
@@ -710,15 +733,45 @@ ComplexPairTy ComplexExprEmitter::VisitUnaryNot(const UnaryOperator *E) {
llvm::Value *ResI;
if (Op.second->getType()->isFloatingPointTy())
ResI = Builder.CreateFNeg(Op.second, "conj.i");
- else
+ else if (CGF.getTarget().hasSoftX87LongDouble()) {
+ // ARM64EC: flip the sign bit of the imaginary part by way of `double`.
+ SourceLocation Loc;
+ ResI = CGF.EmitDoubleToSoftX87Ld(
+ Builder.CreateFNeg(CGF.EmitSoftX87LdToDouble(Op.second, Loc), "conj.i"),
+ Loc);
+ } else
ResI = Builder.CreateNeg(Op.second, "conj.i");
return ComplexPairTy(Op.first, ResI);
}
+/// Rewrite the components of a soft-x87 `BinOpInfo` as `double`.
+ComplexExprEmitter::BinOpInfo
+ComplexExprEmitter::softX87ComplexToDouble(const BinOpInfo &Op) {
+ SourceLocation Loc;
+ auto ToDouble = [&](llvm::Value *V) -> llvm::Value * {
+ return V ? CGF.EmitSoftX87LdToDouble(V, Loc) : nullptr;
+ };
+ BinOpInfo Out = Op;
+ Out.LHS = ComplexPairTy(ToDouble(Op.LHS.first), ToDouble(Op.LHS.second));
+ Out.RHS = ComplexPairTy(ToDouble(Op.RHS.first), ToDouble(Op.RHS.second));
+ // The ABI lowering of the operands and of the '__muldc3'/'__divdc3' libcalls
+ // is driven by this type, which has to follow the components.
+ Out.Ty = CGF.getContext().getComplexType(CGF.getContext().DoubleTy);
+ return Out;
+}
+
ComplexPairTy ComplexExprEmitter::EmitBinAdd(const BinOpInfo &Op) {
llvm::Value *ResR, *ResI;
+ if (isSoftX87ComplexOp(Op)) {
+ BinOpInfo DblOp = softX87ComplexToDouble(Op);
+ ComplexPairTy Res = EmitBinAdd(DblOp);
+ SourceLocation Loc;
+ return ComplexPairTy(CGF.EmitDoubleToSoftX87Ld(Res.first, Loc),
+ CGF.EmitDoubleToSoftX87Ld(Res.second, Loc));
+ }
+
if (Op.LHS.first->getType()->isFloatingPointTy()) {
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, Op.FPFeatures);
ResR = Builder.CreateFAdd(Op.LHS.first, Op.RHS.first, "add.r");
@@ -738,6 +791,15 @@ ComplexPairTy ComplexExprEmitter::EmitBinAdd(const BinOpInfo &Op) {
ComplexPairTy ComplexExprEmitter::EmitBinSub(const BinOpInfo &Op) {
llvm::Value *ResR, *ResI;
+
+ if (isSoftX87ComplexOp(Op)) {
+ BinOpInfo DblOp = softX87ComplexToDouble(Op);
+ ComplexPairTy Res = EmitBinSub(DblOp);
+ SourceLocation Loc;
+ return ComplexPairTy(CGF.EmitDoubleToSoftX87Ld(Res.first, Loc),
+ CGF.EmitDoubleToSoftX87Ld(Res.second, Loc));
+ }
+
if (Op.LHS.first->getType()->isFloatingPointTy()) {
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, Op.FPFeatures);
ResR = Builder.CreateFSub(Op.LHS.first, Op.RHS.first, "sub.r");
@@ -825,6 +887,14 @@ ComplexPairTy ComplexExprEmitter::EmitBinMul(const BinOpInfo &Op) {
Value *ResR, *ResI;
llvm::MDBuilder MDHelper(CGF.getLLVMContext());
+ if (isSoftX87ComplexOp(Op)) {
+ BinOpInfo DblOp = softX87ComplexToDouble(Op);
+ ComplexPairTy Res = EmitBinMul(DblOp);
+ SourceLocation Loc;
+ return ComplexPairTy(CGF.EmitDoubleToSoftX87Ld(Res.first, Loc),
+ CGF.EmitDoubleToSoftX87Ld(Res.second, Loc));
+ }
+
if (Op.LHS.first->getType()->isFloatingPointTy()) {
// The general formulation is:
// (a + ib) * (c + id) = (a * c - b * d) + i(a * d + b * c)
@@ -1034,6 +1104,15 @@ ComplexPairTy ComplexExprEmitter::EmitBinDiv(const BinOpInfo &Op) {
llvm::Value *LHSr = Op.LHS.first, *LHSi = Op.LHS.second;
llvm::Value *RHSr = Op.RHS.first, *RHSi = Op.RHS.second;
llvm::Value *DSTr, *DSTi;
+
+ if (isSoftX87ComplexOp(Op)) {
+ BinOpInfo DblOp = softX87ComplexToDouble(Op);
+ ComplexPairTy Res = EmitBinDiv(DblOp);
+ SourceLocation Loc;
+ return ComplexPairTy(CGF.EmitDoubleToSoftX87Ld(Res.first, Loc),
+ CGF.EmitDoubleToSoftX87Ld(Res.second, Loc));
+ }
+
if (LHSr->getType()->isFloatingPointTy()) {
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, Op.FPFeatures);
if (!RHSi) {
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index e2ec07bbae711a..1c5606a93d7851 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -56,6 +56,17 @@ llvm::Constant *getPadding(const CodeGenModule &CGM, CharUnits PadSize) {
return llvm::UndefValue::get(Ty);
}
+/// Emit a floating-point value as an IR constant. ARM64EC keeps `long double`
+/// in the software x87 format, where a value is an integer image and not an FP
+/// value, so it must not be emitted as an llvm::ConstantFP.
+static llvm::Constant *emitFloatConstant(CodeGenModule &CGM,
+ const llvm::APFloat &Value) {
+ if (CGM.getTarget().hasSoftX87LongDouble() &&
+ &Value.getSemantics() == &llvm::APFloat::x87DoubleExtended())
+ return CGM.EmitSoftX87LdConstant(Value);
+ return llvm::ConstantFP::get(CGM.getLLVMContext(), Value);
+}
+
struct ConstantAggregateBuilderUtils {
CodeGenModule &CGM;
@@ -2649,14 +2660,12 @@ ConstantEmitter::tryEmitPrivate(const APValue &Value, QualType DestType,
return llvm::ConstantStruct::get(STy, Complex);
}
case APValue::Float:
- return llvm::ConstantFP::get(CGM.getLLVMContext(), Value.getFloat());
+ return emitFloatConstant(CGM, Value.getFloat());
case APValue::ComplexFloat: {
llvm::Constant *Complex[2];
- Complex[0] = llvm::ConstantFP::get(CGM.getLLVMContext(),
- Value.getComplexFloatReal());
- Complex[1] = llvm::ConstantFP::get(CGM.getLLVMContext(),
- Value.getComplexFloatImag());
+ Complex[0] = emitFloatConstant(CGM, Value.getComplexFloatReal());
+ Complex[1] = emitFloatConstant(CGM, Value.getComplexFloatImag());
// FIXME: the target may want to specify that this is packed.
llvm::StructType *STy =
@@ -2672,7 +2681,7 @@ ConstantEmitter::tryEmitPrivate(const APValue &Value, QualType DestType,
if (Elt.isInt())
Inits[I] = llvm::ConstantInt::get(CGM.getLLVMContext(), Elt.getInt());
else if (Elt.isFloat())
- Inits[I] = llvm::ConstantFP::get(CGM.getLLVMContext(), Elt.getFloat());
+ Inits[I] = emitFloatConstant(CGM, Elt.getFloat());
else if (Elt.isIndeterminate())
Inits[I] = llvm::UndefValue::get(CGM.getTypes().ConvertType(
DestType->castAs<VectorType>()->getElementType()));
@@ -2696,8 +2705,7 @@ ConstantEmitter::tryEmitPrivate(const APValue &Value, QualType DestType,
Inits[Idx] =
llvm::ConstantInt::get(CGM.getLLVMContext(), Elt.getInt());
else if (Elt.isFloat())
- Inits[Idx] =
- llvm::ConstantFP::get(CGM.getLLVMContext(), Elt.getFloat());
+ Inits[Idx] = emitFloatConstant(CGM, Elt.getFloat());
else if (Elt.isIndeterminate())
Inits[Idx] = llvm::PoisonValue::get(
CGM.getTypes().ConvertType(MT->getElementType()));
@@ -2962,3 +2970,18 @@ llvm::Constant *
CodeGenModule::EmitNullConstantForBase(const CXXRecordDecl *Record) {
return ::EmitNullConstant(*this, Record, false);
}
+
+llvm::Constant *
+CodeGenModule::EmitSoftX87LdConstant(const llvm::APFloat &Value) {
+ // The memory image of an x87 extended-precision value is the 64-bit
+ // significand followed by the 16-bit sign/exponent word; the remaining 6
+ // bytes of the 16-byte slot are padding. As a little-endian integer, the
+ // image is therefore `significand | (sign_exponent << 64)`, which is exactly
+ // what APFloat::bitcastToAPInt() returns for this format: the constant is
+ // bit-exact, nothing is rounded here, and the padding bytes are zero.
+ assert(&Value.getSemantics() == &llvm::APFloat::x87DoubleExtended() &&
+ "expected the x87 extended format");
+ unsigned Width = getContext().getTypeSize(getContext().LongDoubleTy);
+ return llvm::ConstantInt::get(llvm::IntegerType::get(getLLVMContext(), Width),
+ Value.bitcastToAPInt().zextOrTrunc(Width));
+}
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 0e2e29f0745d27..867605d4f40572 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -555,6 +555,9 @@ class ScalarExprEmitter
return Builder.getInt(E->getValue());
}
Value *VisitFloatingLiteral(const FloatingLiteral *E) {
+ if (CGF.getTarget().hasSoftX87LongDouble() &&
+ E->getType()->isSpecificBuiltinType(BuiltinType::LongDouble))
+ return CGF.CGM.EmitSoftX87LdConstant(E->getValue());
return llvm::ConstantFP::get(VMContext, E->getValue());
}
Value *VisitCharacterLiteral(const CharacterLiteral *E) {
@@ -838,6 +841,9 @@ class ScalarExprEmitter
// Binary Operators.
Value *EmitMul(const BinOpInfo &Ops) {
+ if (CGF.getTarget().hasSoftX87LongDouble() &&
+ Ops.Ty->isSpecificBuiltinType(BuiltinType::LongDouble))
+ return EmitSoftX87BinOp(Ops, &ScalarExprEmitter::EmitMul);
if (Ops.Ty->isSignedIntegerOrEnumerationType() ||
Ops.Ty->isUnsignedIntegerType()) {
const bool isSigned = Ops.Ty->isSignedIntegerOrEnumerationType();
@@ -985,8 +991,17 @@ class ScalarExprEmitter
Value *EmitCompare(const BinaryOperator *E, llvm::CmpInst::Predicate UICmpOpc,
llvm::CmpInst::Predicate SICmpOpc,
llvm::CmpInst::Predicate FCmpOpc, bool IsSignaling);
+ Value *EmitSoftX87Compare(const BinaryOperator *E,
+ llvm::CmpInst::Predicate FCmpOpc);
+ Value *EmitSoftX87BinOp(
+ const BinOpInfo &Ops,
+ Value *(ScalarExprEmitter::*Emit)(const BinOpInfo &));
#define VISITCOMP(CODE, UI, SI, FP, SIG) \
Value *VisitBin##CODE(const BinaryOperator *E) { \
+ if (CGF.getTarget().hasSoftX87LongDouble() && \
+ E->getLHS()->getType()->isSpecificBuiltinType( \
+ BuiltinType::LongDouble)) \
+ return EmitSoftX87Compare(E, llvm::FCmpInst::FP); \
return EmitCompare(E, llvm::ICmpInst::UI, llvm::ICmpInst::SI, \
llvm::FCmpInst::FP, SIG); }
VISITCOMP(LT, ICMP_ULT, ICMP_SLT, FCMP_OLT, true)
@@ -1044,6 +1059,18 @@ class ScalarExprEmitter
Value *ScalarExprEmitter::EmitConversionToBool(Value *Src, QualType SrcType) {
assert(SrcType.isCanonical() && "EmitScalarConversion strips typedefs");
+ // ARM64EC: a soft x87 `long double` is zero if and only if both the
+ // significand and the exponent field of its image are zero, so this test
+ // stays exact even for values below the `double` range.
+ if (CGF.getTarget().hasSoftX87LongDouble() &&
+ SrcType->isSpecificBuiltinType(BuiltinType::LongDouble)) {
+ llvm::Constant *Low79 = llvm::ConstantInt::get(
+ Src->getType(), llvm::APInt::getLowBitsSet(128, 79));
+ return CGF.Builder.CreateICmpNE(
+ CGF.Builder.CreateAnd(Src, Low79),
+ llvm::ConstantInt::get(Src->getType(), 0));
+ }
+
if (SrcType->isRealFloatingType())
return EmitFloatToBoolConversion(Src);
@@ -1629,12 +1656,60 @@ Value *ScalarExprEmitter::EmitScalarCast(Value *Src, QualType SrcType,
return Builder.CreateFPExt(Src, DstTy, "conv");
}
+/// Evaluate a binary operation on soft x87 `long double` operands: both sides
+/// are converted to `double`, the operation is carried out in `double`, and the
+/// result is converted back into the x87 image.
+Value *ScalarExprEmitter::EmitSoftX87BinOp(
+ const BinOpInfo &Ops,
+ Value *(ScalarExprEmitter::*Emit)(const BinOpInfo &)) {
+ SourceLocation Loc = Ops.E ? Ops.E->getExprLoc() : SourceLocation();
+ BinOpInfo DoubleOps = Ops;
+ DoubleOps.Ty = CGF.getContext().DoubleTy;
+ DoubleOps.LHS = CGF.EmitSoftX87LdToDouble(Ops.LHS, Loc);
+ DoubleOps.RHS = CGF.EmitSoftX87LdToDouble(Ops.RHS, Loc);
+ return CGF.EmitDoubleToSoftX87Ld((this->*Emit)(DoubleOps), Loc);
+}
+
+Value *ScalarExprEmitter::EmitSoftX87Compare(const BinaryOperator *E,
+ llvm::CmpInst::Predicate FCmpOpc) {
+ SourceLocation Loc = E->getExprLoc();
+ llvm::Value *LHS =
+ CGF.EmitSoftX87LdToDouble(CGF.EmitScalarExpr(E->getLHS()), Loc);
+ llvm::Value *RHS =
+ CGF.EmitSoftX87LdToDouble(CGF.EmitScalarExpr(E->getRHS()), Loc);
+ llvm::Value *Result = Builder.CreateFCmp(FCmpOpc, LHS, RHS, "cmp");
+ return EmitScalarConversion(Result, CGF.getContext().BoolTy, E->getType(),
+ Loc);
+}
+
/// Emit a conversion from the specified type to the specified destination type,
/// both of which are LLVM scalar types.
Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
QualType DstType,
SourceLocation Loc,
ScalarConversionOpts Opts) {
+ // ARM64EC: `long double` is an x87 image held in a 16-byte integer slot.
+ // Every conversion goes through `double` and the runtime helpers.
+ if (Src && CGF.getTarget().hasSoftX87LongDouble()) {
+ QualType CSrcType = CGF.getContext().getCanonicalType(SrcType);
+ QualType CDstType = CGF.getContext().getCanonicalType(DstType);
+ const bool SrcIsLd =
+ CSrcType->isSpecificBuiltinType(BuiltinType::LongDouble);
+ const bool DstIsLd =
+ CDstType->isSpecificBuiltinType(BuiltinType::LongDouble);
+ if (CSrcType != CDstType && (SrcIsLd || DstIsLd)) {
+ QualType DoubleTy = CGF.getContext().DoubleTy;
+ if (SrcIsLd) {
+ Src = CGF.EmitSoftX87LdToDouble(Src, Loc);
+ SrcType = DoubleTy;
+ }
+ if (DstIsLd) {
+ Src = EmitScalarConversion(Src, SrcType, DoubleTy, Loc, Opts);
+ return CGF.EmitDoubleToSoftX87Ld(Src, Loc);
+ }
+ }
+ }
+
// All conversions involving fixed point types should be handled by the
// EmitFixedPoint family functions. This is done to prevent bloating up this
// function more, and although fixed point numbers are represented by
@@ -3131,7 +3206,13 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
return EmitPointerToBoolConversion(Visit(E), E->getType());
case CK_FloatingToBoolean: {
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE);
- return EmitFloatToBoolConversion(Visit(E));
+ Value *Src = Visit(E);
+ // ARM64EC: a soft x87 `long double` is an integer image, not an FP value,
+ // so it has to be tested with integer operations; see EmitConversionToBool.
+ if (CGF.getTarget().hasSoftX87LongDouble() &&
+ E->getType()->isSpecificBuiltinType(BuiltinType::LongDouble))
+ return EmitConversionToBool(Src, E->getType().getCanonicalType());
+ return EmitFloatToBoolConversion(Src);
}
case CK_MemberPointerToBoolean: {
llvm::Value *MemPtr = Visit(E);
@@ -3561,6 +3642,14 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
llvm::Value *amt;
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, E);
+ // ARM64EC: a soft x87 `long double` is an integer image with no FP
+ // representation, so the increment goes through `double` and the runtime
+ // helpers.
+ const bool SoftX87 = CGF.getTarget().hasSoftX87LongDouble() &&
+ type->isSpecificBuiltinType(BuiltinType::LongDouble);
+ if (SoftX87)
+ value = CGF.EmitSoftX87LdToDouble(value, E->getExprLoc());
+
// Another special case: half FP increment should be done via float.
if (type->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType)
value = Builder.CreateFPExt(value, CGF.CGM.FloatTy, "incdec.conv");
@@ -3597,6 +3686,9 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
if (type->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType)
value = Builder.CreateFPTrunc(value, CGF.CGM.HalfTy, "incdec.conv");
+ if (SoftX87)
+ value = CGF.EmitDoubleToSoftX87Ld(value, E->getExprLoc());
+
// Fixed-point types.
} else if (type->isFixedPointType()) {
// Fixed-point types are tricky. In some cases, it isn't possible to
@@ -3714,6 +3806,16 @@ Value *ScalarExprEmitter::VisitMinus(const UnaryOperator *E,
return Builder.CreateFNeg(Op, "fneg");
}
+ // ARM64EC: negate a soft x87 `long double` by flipping its sign bit. This
+ // keeps the exact bit pattern (in particular -(+0.0) is -0.0, which the
+ // 0.0 - x formulation below would get wrong).
+ if (CGF.getTarget().hasSoftX87LongDouble() &&
+ E->getType()->isSpecificBuiltinType(BuiltinType::LongDouble)) {
+ llvm::APInt SignMask = llvm::APInt::getOneBitSet(128, 79);
+ return Builder.CreateXor(Op, llvm::ConstantInt::get(VMContext, SignMask),
+ "fneg");
+ }
+
// Emit unary minus with EmitSub so we handle overflow cases etc.
BinOpInfo BinOp;
BinOp.RHS = Op;
@@ -4320,6 +4422,9 @@ void ScalarExprEmitter::EmitUndefinedBehaviorIntegerDivAndRemCheck(
}
Value *ScalarExprEmitter::EmitDiv(const BinOpInfo &Ops) {
+ if (CGF.getTarget().hasSoftX87LongDouble() &&
+ Ops.Ty->isSpecificBuiltinType(BuiltinType::LongDouble))
+ return EmitSoftX87BinOp(Ops, &ScalarExprEmitter::EmitDiv);
{
SanitizerDebugLocation SanScope(&CGF,
{SanitizerKind::SO_IntegerDivideByZero,
@@ -4374,6 +4479,9 @@ Value *ScalarExprEmitter::EmitDiv(const BinOpInfo &Ops) {
}
Value *ScalarExprEmitter::EmitRem(const BinOpInfo &Ops) {
+ if (CGF.getTarget().hasSoftX87LongDouble() &&
+ Ops.Ty->isSpecificBuiltinType(BuiltinType::LongDouble))
+ return EmitSoftX87BinOp(Ops, &ScalarExprEmitter::EmitRem);
// Rem in C can't be a floating point type: C99 6.5.5p2.
if ((CGF.SanOpts.has(SanitizerKind::IntegerDivideByZero) ||
CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) &&
@@ -4784,6 +4892,9 @@ static Value* tryEmitFMulAdd(const BinOpInfo &op,
}
Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &op) {
+ if (CGF.getTarget().hasSoftX87LongDouble() &&
+ op.Ty->isSpecificBuiltinType(BuiltinType::LongDouble))
+ return EmitSoftX87BinOp(op, &ScalarExprEmitter::EmitAdd);
if (op.LHS->getType()->isPointerTy() ||
op.RHS->getType()->isPointerTy())
return emitPointerArithmetic(CGF, op, CodeGenFunction::NotSubtraction);
@@ -4946,6 +5057,9 @@ Value *ScalarExprEmitter::EmitFixedPointBinOp(const BinOpInfo &op) {
}
Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
+ if (CGF.getTarget().hasSoftX87LongDouble() &&
+ op.Ty->isSpecificBuiltinType(BuiltinType::LongDouble))
+ return EmitSoftX87BinOp(op, &ScalarExprEmitter::EmitSub);
// The LHS is always a pointer if either side is.
if (!op.LHS->getType()->isPointerTy()) {
if (op.Ty->isSignedIntegerOrEnumerationType() ||
@@ -5415,11 +5529,22 @@ Value *ScalarExprEmitter::EmitCompare(const BinaryOperator *E,
}
Value *ResultR, *ResultI;
- if (CETy->isRealFloatingType()) {
+ if (CETy->isRealFloatingType() &&
+ LHS.first->getType()->isFloatingPointTy()) {
// As complex comparisons can only be equality comparisons, they
// are never signaling comparisons.
ResultR = Builder.CreateFCmp(FCmpOpc, LHS.first, RHS.first, "cmp.r");
ResultI = Builder.CreateFCmp(FCmpOpc, LHS.second, RHS.second, "cmp.i");
+ } else if (CETy->isRealFloatingType()) {
+ // ARM64EC: the element is an x87 image held in an integer, so compare
+ // the components through `double` instead of with an integer predicate.
+ SourceLocation Loc = E->getExprLoc();
+ ResultR = Builder.CreateFCmp(
+ FCmpOpc, CGF.EmitSoftX87LdToDouble(LHS.first, Loc),
+ CGF.EmitSoftX87LdToDouble(RHS.first, Loc), "cmp.r");
+ ResultI = Builder.CreateFCmp(
+ FCmpOpc, CGF.EmitSoftX87LdToDouble(LHS.second, Loc),
+ CGF.EmitSoftX87LdToDouble(RHS.second, Loc), "cmp.i");
} else {
// Complex comparisons can only be equality comparisons. As such, signed
// and unsigned opcodes are the same.
@@ -6231,6 +6356,36 @@ Value *CodeGenFunction::EmitComplexToScalarConversion(ComplexPairTy Src,
.EmitComplexToScalarConversion(Src, SrcTy, DstTy, Loc);
}
+Address CodeGenFunction::CreateSoftX87LdTemp(SourceLocation Loc) {
+ QualType LDTy = getContext().LongDoubleTy;
+ CharUnits Align = getContext().getTypeAlignInChars(LDTy);
+ llvm::Type *Ty = ConvertType(LDTy);
+ return Address(CreateTempAlloca(Ty, Align, "x87ld.tmp"));
+}
+
+llvm::Value *CodeGenFunction::EmitSoftX87LdToDouble(llvm::Value *Bits,
+ SourceLocation Loc) {
+ Address Tmp = CreateSoftX87LdTemp(Loc);
+ Builder.CreateStore(Bits, Tmp);
+ llvm::FunctionType *FT = llvm::FunctionType::get(
+ llvm::Type::getDoubleTy(getLLVMContext()), {Builder.getPtrTy()}, false);
+ llvm::FunctionCallee Fn =
+ CGM.CreateRuntimeFunction(FT, "__arm64ec_load_x87_long_double");
+ return Builder.CreateCall(Fn, {Tmp.getPointerIfNotSigned()},
+ "x87ld.to.double");
+}
+
+llvm::Value *CodeGenFunction::EmitDoubleToSoftX87Ld(llvm::Value *Value,
+ SourceLocation Loc) {
+ Address Tmp = CreateSoftX87LdTemp(Loc);
+ llvm::FunctionType *FT = llvm::FunctionType::get(
+ llvm::Type::getVoidTy(getLLVMContext()),
+ {Builder.getPtrTy(), llvm::Type::getDoubleTy(getLLVMContext())}, false);
+ llvm::FunctionCallee Fn =
+ CGM.CreateRuntimeFunction(FT, "__arm64ec_store_x87_long_double");
+ Builder.CreateCall(Fn, {Tmp.getPointerIfNotSigned(), Value});
+ return Builder.CreateLoad(Tmp, "double.to.x87ld");
+}
Value *
CodeGenFunction::EmitPromotedScalarExpr(const Expr *E,
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index e17ed28c5d4f3a..4fd1e9a8bb5ad2 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -4899,6 +4899,13 @@ class CodeGenFunction : public CodeGenTypeCache {
RValue EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
const CallExpr *E, ReturnValueSlot ReturnValue);
+ /// ARM64EC: lower a builtin that takes or returns a soft x87 `long double`
+ /// without ever creating floating-point values of that type. Returns false
+ /// if the builtin does not need special treatment.
+ bool EmitSoftX87LongDoubleBuiltin(unsigned BuiltinID, const CallExpr *E,
+ ReturnValueSlot ReturnValue,
+ RValue &Result);
+
RValue emitRotate(const CallExpr *E, bool IsRotateRight);
RValue emitStdcCountIntrinsic(const CallExpr *E, llvm::Intrinsic::ID IntID,
@@ -5215,6 +5222,11 @@ class CodeGenFunction : public CodeGenTypeCache {
llvm::Value *EmitScalarConversion(llvm::Value *Src, QualType SrcTy,
QualType DstTy, SourceLocation Loc);
+ /// Emit intermediate conversions for software x86 `long double`.
+ Address CreateSoftX87LdTemp(SourceLocation Loc);
+ llvm::Value *EmitSoftX87LdToDouble(llvm::Value *Bits, SourceLocation Loc);
+ llvm::Value *EmitDoubleToSoftX87Ld(llvm::Value *Value, SourceLocation Loc);
+
/// Emit a conversion from the specified complex type to the specified
/// destination type, where the destination type is an LLVM scalar type.
llvm::Value *EmitComplexToScalarConversion(ComplexPairTy Src, QualType SrcTy,
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index d9f3901c0c67ed..66ee7c590b32c4 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1454,6 +1454,10 @@ class CodeGenModule : public CodeGenTypeCache {
/// null constant.
llvm::Constant *EmitNullConstant(QualType T);
+ /// ARM64EC: return the 16-byte integer image of an x87 extended-precision
+ /// value, which is how `long double` constants are represented.
+ llvm::Constant *EmitSoftX87LdConstant(const llvm::APFloat &Value);
+
/// Return a null constant appropriate for zero-initializing a base class with
/// the given type. This is usually, but not always, an LLVM null constant.
llvm::Constant *EmitNullConstantForBase(const CXXRecordDecl *Record);
diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp
index daed8559cc62f5..97a96e8fb1e82d 100644
--- a/clang/lib/CodeGen/CodeGenTypes.cpp
+++ b/clang/lib/CodeGen/CodeGenTypes.cpp
@@ -477,6 +477,13 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
break;
case BuiltinType::LongDouble:
LongDoubleReferenced = true;
+ if (getTarget().hasSoftX87LongDouble()) {
+ // For ARM64EC, the software x87 `long double` is an integer image
+ // of the value in a 16-byte slot. All computation goes through
+ // `double` via runtime helper calls.
+ ResultType = llvm::IntegerType::get(getLLVMContext(), 128);
+ break;
+ }
[[fallthrough]];
case BuiltinType::BFloat16:
case BuiltinType::Float:
>From 2d9ce757e6239e06919c467cd4408ff0faf093ba Mon Sep 17 00:00:00 2001
From: LIU Hao <lh_mouse at 126.com>
Date: Wed, 28 Oct 2026 15:22:40 +0800
Subject: [PATCH 6/7] Skip libcxx tests for precision of software x87 `long
double`
Assisted-by: DeepSeek V4.1 Flash
Signed-off-by: LIU Hao <lh_mouse at 126.com>
---
libcxx/test/std/numerics/c.math/cmath.pass.cpp | 5 +++++
.../numeric.ops/numeric.ops.midpoint/midpoint.float.pass.cpp | 5 +++++
2 files changed, 10 insertions(+)
diff --git a/libcxx/test/std/numerics/c.math/cmath.pass.cpp b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
index 8e3a7255e426b0..d19a6053e0aef7 100644
--- a/libcxx/test/std/numerics/c.math/cmath.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
@@ -1122,6 +1122,11 @@ void test_fmin()
struct TestHypot3 {
template <class Real>
void operator()() const {
+#if defined(__arm64ec__) && __LDBL_MANT_DIG__ == 64
+ if constexpr (std::is_same_v<Real, long double>)
+ return;
+#endif
+
const auto check = [](Real elem, Real abs_tol) {
assert(std::isfinite(std::hypot(elem, Real(0), Real(0))));
assert(fptest_close(std::hypot(elem, Real(0), Real(0)), elem, abs_tol));
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.midpoint/midpoint.float.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.midpoint/midpoint.float.pass.cpp
index 7a795d37986b6f..c9f3c39279b3c6 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.midpoint/midpoint.float.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.midpoint/midpoint.float.pass.cpp
@@ -77,6 +77,11 @@ constexpr bool test_ppc_edge_cases() {
template <typename T>
constexpr bool test_floating_points() {
+#if defined(__arm64ec__) && __LDBL_MANT_DIG__ == 64
+ if constexpr (std::same_as<T, long double>)
+ return true;
+#endif
+
ASSERT_SAME_TYPE(T, decltype(std::midpoint(T{}, T{})));
ASSERT_NOEXCEPT(std::midpoint(T{}, T{}));
>From 6b2b091a2b59f8f3175d23982a7a26970073f3a4 Mon Sep 17 00:00:00 2001
From: LIU Hao <lh_mouse at 126.com>
Date: Wed, 28 Oct 2026 20:58:27 +0800
Subject: [PATCH 7/7] apply code formatter diff
Signed-off-by: LIU Hao <lh_mouse at 126.com>
---
clang/lib/CodeGen/CGExprScalar.cpp | 27 +++++++++----------
.../test/std/numerics/c.math/cmath.pass.cpp | 4 +--
2 files changed, 15 insertions(+), 16 deletions(-)
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 867605d4f40572..da1812af95864b 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -993,17 +993,17 @@ class ScalarExprEmitter
llvm::CmpInst::Predicate FCmpOpc, bool IsSignaling);
Value *EmitSoftX87Compare(const BinaryOperator *E,
llvm::CmpInst::Predicate FCmpOpc);
- Value *EmitSoftX87BinOp(
- const BinOpInfo &Ops,
- Value *(ScalarExprEmitter::*Emit)(const BinOpInfo &));
-#define VISITCOMP(CODE, UI, SI, FP, SIG) \
- Value *VisitBin##CODE(const BinaryOperator *E) { \
- if (CGF.getTarget().hasSoftX87LongDouble() && \
- E->getLHS()->getType()->isSpecificBuiltinType( \
- BuiltinType::LongDouble)) \
- return EmitSoftX87Compare(E, llvm::FCmpInst::FP); \
- return EmitCompare(E, llvm::ICmpInst::UI, llvm::ICmpInst::SI, \
- llvm::FCmpInst::FP, SIG); }
+ Value *EmitSoftX87BinOp(const BinOpInfo &Ops,
+ Value *(ScalarExprEmitter::*Emit)(const BinOpInfo &));
+#define VISITCOMP(CODE, UI, SI, FP, SIG) \
+ Value *VisitBin##CODE(const BinaryOperator *E) { \
+ if (CGF.getTarget().hasSoftX87LongDouble() && \
+ E->getLHS()->getType()->isSpecificBuiltinType( \
+ BuiltinType::LongDouble)) \
+ return EmitSoftX87Compare(E, llvm::FCmpInst::FP); \
+ return EmitCompare(E, llvm::ICmpInst::UI, llvm::ICmpInst::SI, \
+ llvm::FCmpInst::FP, SIG); \
+ }
VISITCOMP(LT, ICMP_ULT, ICMP_SLT, FCMP_OLT, true)
VISITCOMP(GT, ICMP_UGT, ICMP_SGT, FCMP_OGT, true)
VISITCOMP(LE, ICMP_ULE, ICMP_SLE, FCMP_OLE, true)
@@ -1066,9 +1066,8 @@ Value *ScalarExprEmitter::EmitConversionToBool(Value *Src, QualType SrcType) {
SrcType->isSpecificBuiltinType(BuiltinType::LongDouble)) {
llvm::Constant *Low79 = llvm::ConstantInt::get(
Src->getType(), llvm::APInt::getLowBitsSet(128, 79));
- return CGF.Builder.CreateICmpNE(
- CGF.Builder.CreateAnd(Src, Low79),
- llvm::ConstantInt::get(Src->getType(), 0));
+ return CGF.Builder.CreateICmpNE(CGF.Builder.CreateAnd(Src, Low79),
+ llvm::ConstantInt::get(Src->getType(), 0));
}
if (SrcType->isRealFloatingType())
diff --git a/libcxx/test/std/numerics/c.math/cmath.pass.cpp b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
index d19a6053e0aef7..eb357104736ab3 100644
--- a/libcxx/test/std/numerics/c.math/cmath.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
@@ -1122,10 +1122,10 @@ void test_fmin()
struct TestHypot3 {
template <class Real>
void operator()() const {
-#if defined(__arm64ec__) && __LDBL_MANT_DIG__ == 64
+# if defined(__arm64ec__) && __LDBL_MANT_DIG__ == 64
if constexpr (std::is_same_v<Real, long double>)
return;
-#endif
+# endif
const auto check = [](Real elem, Real abs_tol) {
assert(std::isfinite(std::hypot(elem, Real(0), Real(0))));
More information about the libcxx-commits
mailing list