[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 06:07:28 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/8] 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/8] 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/8] 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/8] 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/8] 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/8] 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/8] 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))));

>From 72910cbb3c99ceead3cb3ec3885232984c0a5da5 Mon Sep 17 00:00:00 2001
From: LIU Hao <lh_mouse at 126.com>
Date: Wed, 28 Oct 2026 21:06:37 +0800
Subject: [PATCH 8/8] remove `static` from `emitFloatConstant`; it is in
 unnamed namespace

Signed-off-by: LIU Hao <lh_mouse at 126.com>
---
 clang/lib/CodeGen/CGExprConstant.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 1c5606a93d7851..6ae05dbb5bc992 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -59,8 +59,8 @@ llvm::Constant *getPadding(const CodeGenModule &CGM, CharUnits PadSize) {
 /// 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) {
+llvm::Constant *emitFloatConstant(CodeGenModule &CGM,
+                                  const llvm::APFloat &Value) {
   if (CGM.getTarget().hasSoftX87LongDouble() &&
       &Value.getSemantics() == &llvm::APFloat::x87DoubleExtended())
     return CGM.EmitSoftX87LdConstant(Value);



More information about the libcxx-commits mailing list