llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-aarch64

Author: LIU Hao (lhmouse)

<details>
<summary>Changes</summary>

This commits implement the three options for arm64ec-w64-mingw32. 
`-mlong-double-80` is made default to fix the interoperability bug with 
x86_64-w64-mingw32. This also introduces an ABI break with old ARM64EC binaries.

`-mlong-double-64` is the old behavior that is compativle with 
aarch64-w64-mingw32 and MSVC. `-mlong-double-128` enables float128 in software, 
same as x86-64.

The new, default option `-mlong-double-80` changes `long double` to a pseudo 
floating-point type: The type exists in the frontend only; for the middle-end 
and backend, it's an 128-bit integer that provides storage for a value. The way 
how the frontend emit IR for an operation varies:

1. The ABI of such `long double` is the same with an 128-bit integer. For 
x86-64 it's passed and returned in memory.
2. Load/store operations are performed with integer instructions, and are exact.
3. Bitwise operations, such as `fabsl` and `isnan`, are performed directly in 
the frontend with integer instructions.
4. Arithmetic operations of builtin operators are performed by first converting 
`long double` to `double`, doing the arithmetic, then converting the result 
back. This matches how x86-64 emulation works by default on Windows on ARM64 
with only 53-bit precision, and should be more efficient than a strict softmath 
implementation, which the AArch64 backend doesn't support yet.
5. Other builtin functions result in external calls to mingw-w64 CRT directly.

These changes have been verified on my ARM64 tablet with a patched mingw-w64 
CRT. No regressions are introduced.

Assisted-by: DeepSeek V4.1 Flash

---

Patch is 75.67 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/237057.diff


14 Files Affected:

- (modified) clang/include/clang/Basic/TargetInfo.h (+6) 
- (modified) clang/lib/Basic/Targets/AArch64.cpp (+17) 
- (modified) clang/lib/Basic/Targets/AArch64.h (+5) 
- (modified) clang/lib/CodeGen/CGBuiltin.cpp (+428) 
- (modified) clang/lib/CodeGen/CGExpr.cpp (+13-2) 
- (modified) clang/lib/CodeGen/CGExprComplex.cpp (+101-6) 
- (modified) clang/lib/CodeGen/CGExprConstant.cpp (+19-8) 
- (modified) clang/lib/CodeGen/CGExprScalar.cpp (+369-14) 
- (modified) clang/lib/CodeGen/CodeGenFunction.h (+17) 
- (modified) clang/lib/CodeGen/CodeGenTypes.cpp (+7) 
- (modified) clang/lib/Driver/ToolChains/Clang.cpp (+3) 
- (added) clang/test/CodeGen/arm64ec-long-double-modes.c (+31) 
- (added) clang/test/CodeGen/arm64ec-soft-x87-long-double.c (+160) 
- (added) clang/test/Driver/arm64ec-long-double.c (+21) 


``````````diff
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..01da8b879df053 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,14 @@ WindowsARM64TargetInfo::WindowsARM64TargetInfo(const 
llvm::Triple &Triple,
   SizeType = UnsignedLongLong;
   PtrDiffType = SignedLongLong;
   IntPtrType = SignedLongLong;
+
+  if (Triple.isWindowsArm64EC() && !Triple.isWindowsMSVCEnvironment()) {
+    // arm64ec-w64-mingw32 should be interoperable with x86_64-w64-mingw32,
+    // which by default has `long double` as the x87-specific extended
+    // precision format.
+    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
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index e154b9e752fd5c..9b3b28c85a03e3 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -2714,12 +2714,440 @@ 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.  Return false when the
+/// builtin does not involve such a value.
+bool CodeGenFunction::EmitSoftX87LongDoubleBuiltin(unsigned BuiltinID,
+                                                   const CallExpr *E,
+                                                   RValue &Result) {
+  // The 'nan' and 'inf' families 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.  Match the bare name, since some of
+  // them are also reachable with a `__builtin_` prefix.
+  std::string Name = getContext().BuiltinInfo.getName(BuiltinID);
+  StringRef BaseName = Name;
+  BaseName.consume_front("__builtin_");
+  bool ProducesLongDouble = false;
+  for (const char *Str : {"nanl", "nansl", "infl", "huge_vall"})
+    ProducesLongDouble |= BaseName == Str;
+
+  // Everything below rewrites a call into its binary64 equivalent, which only
+  // makes sense when a `long double` is *consumed*.
+  bool ConsumesLongDouble = false;
+  for (const Expr *Arg : E->arguments()) {
+    QualType Ty = Arg->getType();
+    if (const auto *CTy = Ty->getAs<ComplexType>())
+      Ty = CTy->getElementType();
+    ConsumesLongDouble |= Ty->isSpecificBuiltinType(BuiltinType::LongDouble);
+  }
+
+  if (!ProducesLongDouble && !ConsumesLongDouble)
+    return false;
+
+  // `fabsl`: Set the sign bit to zero.
+  if (BaseName == "fabsl") {
+    if (!E->getType()->isSpecificBuiltinType(BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Constant *MagnitudeMask = llvm::ConstantInt::get(
+        V->getType(), ~llvm::APInt::getOneBitSet(128, 79));
+    Result = RValue::get(Builder.CreateAnd(V, MagnitudeMask));
+    return true;
+  }
+
+  // `copysignl`: Take the magnitude from the first operand and the sign bit
+  // from the second one.
+  if (BaseName == "copysignl") {
+    if (!E->getType()->isSpecificBuiltinType(BuiltinType::LongDouble))
+      return false;
+    llvm::Value *X = EmitScalarExpr(E->getArg(0));
+    llvm::Value *Y = EmitScalarExpr(E->getArg(1));
+    llvm::Constant *MagnitudeMask = llvm::ConstantInt::get(
+        X->getType(), ~llvm::APInt::getOneBitSet(128, 79));
+    llvm::Value *Magnitude = Builder.CreateAnd(X, MagnitudeMask);
+    llvm::Value *Sign = Builder.CreateAnd(Y, Builder.CreateNot(MagnitudeMask));
+    Result = RValue::get(Builder.CreateOr(Magnitude, Sign));
+    return true;
+  }
+
+  // `signbit`: Take the sign bit and normalize it to boolean.
+  if (Name == "__builtin_signbit" || BaseName == "signbitl") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Value *Sign =
+        Builder.CreateTrunc(Builder.CreateLShr(V, 79), Builder.getInt1Ty());
+    Result = RValue::get(Builder.CreateZExt(Sign, ConvertType(E->getType())));
+    return true;
+  }
+
+  // `__builtin_isnan`: Check if the value is a NaN.  An unnormal or pseudo
+  // denormal image is undefined behavior, since it will never be produced in a
+  // constant expression.
+  if (Name == "__builtin_isnan") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Constant *MagnitudeMask = llvm::ConstantInt::get(
+        V->getType(), ~llvm::APInt::getOneBitSet(128, 79));
+    llvm::Value *Magnitude = Builder.CreateAnd(V, MagnitudeMask);
+    llvm::APInt InfinityImage(128, 0x8000000000000000ull);
+    InfinityImage |= llvm::APInt(128, 0x7fffull) << 64;
+    llvm::Constant *Infinity =
+        llvm::ConstantInt::get(V->getType(), InfinityImage);
+    llvm::Value *IsNan = Builder.CreateICmpUGT(Magnitude, Infinity);
+    Result = RValue::get(Builder.CreateZExt(IsNan, ConvertType(E->getType())));
+    return true;
+  }
+
+  // `__builtin_isinf`: Check if the value is a (positive or negative) 
infinity.
+  if (Name == "__builtin_isinf") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Constant *MagnitudeMask = llvm::ConstantInt::get(
+        V->getType(), ~llvm::APInt::getOneBitSet(128, 79));
+    llvm::Value *Magnitude = Builder.CreateAnd(V, MagnitudeMask);
+    llvm::APInt InfinityImage(128, 0x8000000000000000ull);
+    InfinityImage |= llvm::APInt(128, 0x7fffull) << 64;
+    llvm::Constant *Infinity =
+        llvm::ConstantInt::get(V->getType(), InfinityImage);
+    llvm::Value *IsInf = Builder.CreateICmpEQ(Magnitude, Infinity);
+    Result = RValue::get(Builder.CreateZExt(IsInf, ConvertType(E->getType())));
+    return true;
+  }
+
+  // `__builtin_isinf_sign`: Return 1 or -1 depending on the sign of an
+  // infinity, and zero for everything else.
+  if (Name == "__builtin_isinf_sign") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Constant *MagnitudeMask = llvm::ConstantInt::get(
+        V->getType(), ~llvm::APInt::getOneBitSet(128, 79));
+    llvm::Value *Magnitude = Builder.CreateAnd(V, MagnitudeMask);
+    llvm::APInt InfinityImage(128, 0x8000000000000000ull);
+    InfinityImage |= llvm::APInt(128, 0x7fffull) << 64;
+    llvm::Constant *Infinity =
+        llvm::ConstantInt::get(V->getType(), InfinityImage);
+    llvm::Type *IntTy = ConvertType(E->getType());
+    llvm::Value *Sign =
+        Builder.CreateTrunc(Builder.CreateLShr(V, 79), Builder.getInt1Ty());
+    llvm::Value *Signed = Builder.CreateSelect(
+        Sign, llvm::ConstantInt::get(IntTy, -1, /*isSigned=*/true),
+        llvm::ConstantInt::get(IntTy, 1));
+    llvm::Value *IsInf = Builder.CreateICmpEQ(Magnitude, Infinity);
+    Result = RValue::get(
+        Builder.CreateSelect(IsInf, Signed, llvm::ConstantInt::get(IntTy, 0)));
+    return true;
+  }
+
+  // `__builtin_isfinite`: Check if the value is neither an infinity nor a NaN.
+  if (Name == "__builtin_isfinite") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Constant *MagnitudeMask = llvm::ConstantInt::get(
+        V->getType(), ~llvm::APInt::getOneBitSet(128, 79));
+    llvm::Value *Magnitude = Builder.CreateAnd(V, MagnitudeMask);
+    llvm::APInt InfinityImage(128, 0x8000000000000000ull);
+    InfinityImage |= llvm::APInt(128, 0x7fffull) << 64;
+    llvm::Constant *Infinity =
+        llvm::ConstantInt::get(V->getType(), InfinityImage);
+    llvm::Value *IsFinite = Builder.CreateICmpULT(Magnitude, Infinity);
+    Result =
+        RValue::get(Builder.CreateZExt(IsFinite, ConvertType(E->getType())));
+    return true;
+  }
+
+  // `__builtin_isnormal`: Check if the value is finite and nonzero.  A
+  // canonical image has the explicit integer bit set when the exponent is
+  // nonzero, so the exponent alone decides; an unnormal is undefined 
behaviour.
+  if (Name == "__builtin_isnormal") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Value *Exp = Builder.CreateAnd(
+        Builder.CreateTrunc(Builder.CreateLShr(V, 64), Builder.getInt16Ty()),
+        Builder.getInt16(0x7FFF));
+    llvm::Value *IsNormal =
+        Builder.CreateAnd(Builder.CreateICmpNE(Exp, Builder.getInt16(0)),
+                          Builder.CreateICmpNE(Exp, Builder.getInt16(0x7FFF)));
+    Result =
+        RValue::get(Builder.CreateZExt(IsNormal, ConvertType(E->getType())));
+    return true;
+  }
+
+  // `__builtin_fpclassify`: Pick the first five arguments that correspond to a
+  // NaN, an infinity, a normal, a subnormal and a zero respectively.
+  if (Name == "__builtin_fpclassify") {
+    if (!E->getArg(5)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(5));
+    llvm::Value *Exp = Builder.CreateAnd(
+        Builder.CreateTrunc(Builder.CreateLShr(V, 64), Builder.getInt16Ty()),
+        Builder.getInt16(0x7FFF));
+    llvm::Value *Man = Builder.CreateTrunc(V, Builder.getInt64Ty());
+    llvm::Value *ExpZero = Builder.CreateICmpEQ(Exp, Builder.getInt16(0));
+    llvm::Value *ExpAllOnes =
+        Builder.CreateICmpEQ(Exp, Builder.getInt16(0x7FFF));
+    llvm::Value *ManZero = Builder.CreateICmpEQ(Man, Builder.getInt64(0));
+    // Infinity is the all-ones exponent with nothing but the integer bit set;
+    // any other value with that exponent is a NaN.  An unnormal, whose integer
+    // bit is clear, is undefined behaviour.
+    llvm::Value *IsInf = Builder.CreateAnd(
+        ExpAllOnes,
+        Builder.CreateICmpEQ(Man, Builder.getInt64(0x8000000000000000ull)));
+    llvm::Value *IsNaN =
+        Builder.CreateAnd(ExpAllOnes, Builder.CreateNot(IsInf));
+    llvm::Value *IsNormal = Builder.CreateAnd(Builder.CreateNot(ExpZero),
+                                              Builder.CreateNot(ExpAllOnes));
+    llvm::Value *IsSubnormal =
+        Builder.CreateAnd(ExpZero, Builder.CreateNot(ManZero));
+    llvm::Value *R = EmitScalarExpr(E->getArg(4)); // zero
+    R = Builder.CreateSelect(IsSubnormal, EmitScalarExpr(E->getArg(3)), R);
+    R = Builder.CreateSelect(IsNormal, EmitScalarExpr(E->getArg(2)), R);
+    R = Builder.CreateSelect(IsInf, EmitScalarExpr(E->getArg(1)), R);
+    R = Builder.CreateSelect(IsNaN, EmitScalarExpr(E->getArg(0)), R);
+    Result = RValue::get(R);
+    return true;
+  }
+
+  // `infl` and `huge_vall`: Infinity has the significand 0x8000000000000000
+  // with an all-ones exponent, and the six padding bytes are zeroes.  They
+  // take no argument at all, so there is nothing to check.
+  if (BaseName == "infl" || BaseName == "huge_vall") {
+    llvm::APInt Image(128, 0x8000000000000000ull);
+    Image |= llvm::APInt(128, 0x7fffull) << 64;
+    llvm::Type *ImageTy = llvm::IntegerType::get(getLLVMContext(), 128);
+    Result = RValue::get(llvm::ConstantInt::get(ImageTy, Image));
+    return true;
+  }
+
+  // `nanl` and `nansl`: The quiet NaN has the significand 0xc000000000000000
+  // and the signalling one has 0xa000000000000000, both with an all-ones
+  // exponent.  The payload of the tag argument is not propagated, but the
+  // argument is still evaluated, since it may have side effects.
+  if (BaseName == "nanl" || BaseName == "nansl") {
+    if (!E->getArg(0)->getType()->isPointerType())
+      return false;
+    EmitScalarExpr(E->getArg(0));
+    llvm::APInt Image(128, BaseName == "nansl" ? 0xa000000000000000ull
+                                               : 0xc000000000000000ull);
+    Image |= llvm::APInt(128, 0x7fffull) << 64;
+    llvm::Type *ImageTy = llvm::IntegerType::get(getLLVMContext(), 128);
+    Result = RValue::get(llvm::ConstantInt::get(ImageTy, Image));
+    return true;
+  }
+
+  // `__builtin_isunordered`: True if either operand is a NaN.
+  // `__builtin_isgreater`, `__builtin_isgreaterequal`, `__builtin_isless` and
+  // `__builtin_islessequal`: Perform ordered comparisons of two operands.
+  // `__builtin_islessgreater`: True if the operands are ordered and unequal;
+  // that is, if either of `x < y` and `x > y` holds.
+  struct BuiltinComparison {
+    const char *Name;
+    CmpInst::Predicate Predicate;
+  };
+  static constexpr BuiltinComparison BuiltinComparisons[] = {
+      {"__builtin_isunordered", CmpInst::FCMP_UNO},
+      {"__builtin_isgreater", CmpInst::FCMP_OGT},
+      {"__builtin_isgreaterequal", CmpInst::FCMP_OGE},
+      {"__builtin_isless", CmpInst::FCMP_OLT},
+      {"__builtin_islessequal", CmpInst::FCMP_OLE},
+      {"__builtin_islessgreater", CmpInst::FCMP_ONE},
+  };
+  for (const auto &BC : BuiltinComparisons)
+    if (Name == BC.Name) {
+      if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+              BuiltinType::LongDouble) ||
+          !E->getArg(1)->getType()->isSpecificBuiltinType(
+              BuiltinType::LongDouble))
+        return false;
+      llvm::Value *A = EmitScalarExpr(E->getArg(0));
+      llvm::Value *B = EmitScalarExpr(E->getArg(1));
+      llvm::Value *Cmp = EmitSoftX87LongDoubleComparison(A, B, BC.Predicate);
+      Result = RValue::get(Builder.CreateZExt(Cmp, ConvertType(E->getType())));
+      return true;
+    }
+
+  // `__builtin_isfpclass`: Test the image against a mask of class bits.  The
+  // second argument is an integer constant expression built out of the
+  // `__FPCLASS_*` values, whose bits number the classes in this order:
+  // signalling NaN, quiet NaN, -inf, negative normal, negative subnormal,
+  // -0, +0, positive subnormal, positive normal and +inf.  So the value only
+  // has to be mapped to the ordinal of the class it belongs to, and the bit
+  // of the mask with that index decides.
+  if (Name == "__builtin_isfpclass") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    Expr::EvalResult MaskResult;
+    if (!E->getArg(1)->EvaluateAsInt(MaskResult, getContext()))
+      return false;
+    const uint64_t Mask = MaskResult.Val.getInt().getLimitedValue();
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Value *Exp = Builder.CreateAnd(
+        Builder.CreateTrunc(Builder.CreateLShr(V, 64), Builder.getInt16Ty()),
+        Builder.getInt16(0x7FFF));
+    llvm::Value *Man = Builder.CreateTrunc(V, Builder.getInt64Ty());
+    llvm::Value *Negative =
+        Builder.CreateTrunc(Builder.CreateLShr(V, 79), Builder.getInt1Ty());
+    llvm::Value *ExpZero = Builder.CreateICmpEQ(Exp, Builder.getInt16(0));
+    llvm::Value *ExpAllOnes =
+        Builder.CreateICmpEQ(Exp, Builder.getInt16(0x7FFF));
+    llvm::Value *ManZero = Builder.CreateICmpEQ(Man, Builder.getInt64(0));
+    // Infinity is the all-ones exponent with nothing but the integer bit
+    // set, and a NaN is any other image with that exponent, quiet when bit
+    // 62 of the significand is set.  An unnormal, whose integer bit is
+    // clear, is undefined behaviour.
+    llvm::Value *Infinity = Builder.CreateAnd(
+        ExpAllOnes,
+        Builder.CreateICmpEQ(Man, Builder.getInt64(0x8000000000000000ull)));
+    llvm::Value *Quiet = Builder.CreateICmpNE(
+        Builder.CreateAnd(Man, Builder.getInt64(0x4000000000000000ull)),
+        Builder.getInt64(0));
+    // The magnitudes are numbered 2 to 5 from the largest downwards, and the
+    // positive classes continue the sequence in the reverse order, which makes
+    // them `11 - Magnitude`.  A NaN takes 0 or 1 instead of a magnitude.
+    llvm::Value *Magnitude = Builder.getInt64(2);
+    Magnitude = Builder.CreateSelect(Builder.CreateNot(ExpAllOnes),
+                                     Builder.getInt64(3), Magnitude);
+    Magnitude = Builder.CreateSelect(ExpZero, Builder.getInt64(4), Magnitude);
+    Magnitude = Builder.CreateSelect(Builder.CreateAnd(ExpZero, ManZero),
+                                     Builder.getInt64(5), Magnitude);
+    Magnitude = Builder.CreateSelect(
+        Negative, Magnitude,
+        Builder.CreateSub(Builder.getInt64(11), Magnitude));
+    llvm::Value *Ordinal =
+        Builder.CreateSelect(Quiet, Builder.getInt64(1), Builder.getInt64(0));
+    Ordinal = Builder.CreateSelect(
+        Builder.CreateAnd(ExpAllOnes, Builder.CreateNot(Infinity)), Ordinal,
+        Magnitude);
+    llvm::Value *Bit =
+        Builder.CreateAnd(Builder.CreateLShr(Builder.getInt64(Mask), Ordinal),
+                          Builder.getInt64(1));
+    Result = RValue::get(
+        Builder.CreateZExt(Builder.CreateICmpNE(Bit, Builder.getInt64(0)),
+                           ConvertType(E->getType())));
+    return true;
+  }
+
+  // `__builtin_complex`: Assemble a complex value out of a real and an
+  // imaginary part, which become the low and high halves of the image pair.
+  // Its operands are "custom type checked", so they are not promoted: both
+  // have the same real floating type already, and they have to be the `long
+  // double` one for this lowering to apply.
+  if (Name == "__builtin_complex") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble) ||
+        !E->getArg(1)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *Real = EmitScalarE...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/237057
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to